diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 8d83327..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "name": "STERN TOURS (Dev Container)", - // 1. DIES IST DER WICHTIGSTE TEIL: - // Wir verwenden Docker Compose für alle Services - "dockerComposeFile": [ - "../docker-compose.yml" - ], - "service": "laravel.test", - // 3. WIR DEFINIEREN DEN ARBEITSBEREICH: - // Das ist der Pfad, in dem Ihr Code *innerhalb* des Containers liegt. - "workspaceFolder": "/var/www/html", - // 4. WIR LEGEN DEN BENUTZER FEST: - // Laravel Sail führt Befehle standardmäßig als 'sail'-Benutzer aus, um Berechtigungsprobleme zu vermeiden. - "remoteUser": "sail", - // 5. ZUSÄTZLICHE ENTWICKLER-TOOLS (FEATURES): - // Features werden über postCreateCommand installiert um Kompatibilitätsprobleme zu vermeiden - "features": {}, - // 6. BEFEHLE NACH DEM ERSTELLEN: - // Installiert nur die Tools die ohne Root-Rechte funktionieren - //"postCreateCommand": "composer install --no-interaction --prefer-dist --optimize-autoloader", - // 7. EDITOR-ANPASSUNGEN (Optional, aber sehr empfohlen): - "customizations": { - "vscode": { - "extensions": [ - "bmewburn.vscode-intelephense-client", - "onecentlin.laravel-blade", - "shufo.vscode-blade-formatter", - "bradlc.vscode-tailwindcss" - ] - } - }, - // 8. ZU STARTENDE DIENSTE: - // Legt fest, welche Dienste aus der docker-compose.yml gestartet werden sollen. - "runServices": [ - "laravel.test", - "mysql", - "mysql-stern", - "redis", - "mailpit" - ], - // 9. ZUSÄTZLICHE KONFIGURATION: - // Umgebungsvariablen für den DevContainer - "containerEnv": { - "WWWUSER": "501", - "WWWGROUP": "20", - "LARAVEL_SAIL": "1" - }, - // 10. MOUNT-KONFIGURATION: - // Stellt sicher, dass der Code korrekt gemountet wird - "mounts": [ - "source=${localWorkspaceFolder},target=/var/www/html,type=bind,consistency=cached" - ], - // 11. FORWARD PORTS: - // Ports die automatisch weitergeleitet werden sollen - "forwardPorts": [ - 33064, - 33065, - 6379, - 1030, - 8030 - ], - "portsAttributes": { - "33064": { - "label": "MySQL", - "onAutoForward": "silent" - }, - "33065": { - "label": "MySQL Stern", - "onAutoForward": "silent" - }, - "6379": { - "label": "Redis", - "onAutoForward": "silent" - }, - "1030": { - "label": "Mailpit SMTP", - "onAutoForward": "silent" - }, - "8030": { - "label": "Mailpit Dashboard", - "onAutoForward": "notify" - } - } -} diff --git a/.env b/.env index a8d7b6d..8aae097 100755 --- a/.env +++ b/.env @@ -15,19 +15,21 @@ APP_DOMAIN_TLD=test LOG_CHANNEL=stack +SUCCESS_KEY=f6077389c9ce710e554763a5de02c8ec + # Standard Database Connection DB_CONNECTION=mysql -DB_HOST=mysql +DB_HOST=global-mysql DB_PORT=3306 DB_DATABASE=stern_crm -DB_USERNAME=sail +DB_USERNAME=root DB_PASSWORD=password # STERN Database Connection -DB_HOST_STERN=mysql-stern +DB_HOST_STERN=global-mysql DB_PORT_STERN=3306 DB_DATABASE_STERN=stern_db -DB_USERNAME_STERN=sail +DB_USERNAME_STERN=root DB_PASSWORD_STERN=password # Docker Port Forwards diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..3cceeb6 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,27 @@ +{ + "mcpServers": { + "laravel-boost": { + "command": "php", + "args": [ + "artisan", + "boost:mcp" + ] + }, + "context7": { + "command": "npx", + "args": [ + "-y", + "@upstash/context7-mcp", + "--api-key", + "ctx7sk-119cd4ab-8983-4229-8702-e84c59c34fc9" + ] + }, + "sequential-thinking": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-sequential-thinking" + ] + } + } +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bb9ae16 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,181 @@ + +=== foundation rules === + +# Laravel Boost Guidelines + +The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications. + +## Foundational Context +This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. + +- php - 8.3.30 +- laravel/framework (LARAVEL) - v10 +- laravel/passport (PASSPORT) - v11 +- laravel/prompts (PROMPTS) - v0 +- laravel/mcp (MCP) - v0 +- laravel/sail (SAIL) - v1 +- phpunit/phpunit (PHPUNIT) - v10 + +## Conventions +- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming. +- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`. +- Check for existing components to reuse before writing a new one. + +## Verification Scripts +- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important. + +## Application Structure & Architecture +- Stick to existing directory structure; don't create new base folders without approval. +- Do not change the application's dependencies without approval. + +## Frontend Bundling +- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them. + +## Replies +- Be concise in your explanations - focus on what's important rather than explaining obvious details. + +## Documentation Files +- You must only create documentation files if explicitly requested by the user. + +=== boost rules === + +## Laravel Boost +- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. + +## Artisan +- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters. + +## URLs +- Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port. + +## Tinker / Debugging +- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. +- Use the `database-query` tool when you only need to read from the database. + +## Reading Browser Logs With the `browser-logs` Tool +- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost. +- Only recent browser logs will be useful - ignore old logs. + +## Searching Documentation (Critically Important) +- Boost comes with a powerful `search-docs` tool you should use before any other approaches when dealing with Laravel or Laravel ecosystem packages. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages. +- The `search-docs` tool is perfect for all Laravel-related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc. +- You must use this tool to search for Laravel ecosystem documentation before falling back to other approaches. +- Search the documentation before making code changes to ensure we are taking the correct approach. +- Use multiple, broad, simple, topic-based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`. +- Do not add package names to queries; package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`. + +### Available Search Syntax +- You can and should pass multiple queries at once. The most relevant results will be returned first. + +1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'. +2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit". +3. Quoted Phrases (Exact Position) - query="infinite scroll" - words must be adjacent and in that order. +4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit". +5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms. + +=== php rules === + +## PHP + +- Always use curly braces for control structures, even if it has one line. + +### Constructors +- Use PHP 8 constructor property promotion in `__construct()`. + - public function __construct(public GitHub $github) { } +- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private. + +### Type Declarations +- Always use explicit return type declarations for methods and functions. +- Use appropriate PHP type hints for method parameters. + + +protected function isAccessible(User $user, ?string $path = null): bool +{ + ... +} + + +## Comments +- Prefer PHPDoc blocks over inline comments. Never use comments within the code itself unless there is something very complex going on. + +## PHPDoc Blocks +- Add useful array shape type definitions for arrays when appropriate. + +## Enums +- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. + +=== laravel/core rules === + +## Do Things the Laravel Way + +- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool. +- If you're creating a generic PHP class, use `php artisan make:class`. +- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior. + +### Database +- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins. +- Use Eloquent models and relationships before suggesting raw database queries. +- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them. +- Generate code that prevents N+1 query problems by using eager loading. +- Use Laravel's query builder for very complex database operations. + +### Model Creation +- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`. + +### APIs & Eloquent Resources +- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention. + +### Controllers & Validation +- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages. +- Check sibling Form Requests to see if the application uses array or string based validation rules. + +### Queues +- Use queued jobs for time-consuming operations with the `ShouldQueue` interface. + +### Authentication & Authorization +- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.). + +### URL Generation +- When generating links to other pages, prefer named routes and the `route()` function. + +### Configuration +- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`. + +### Testing +- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model. +- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`. +- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests. + +### Vite Error +- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`. + +=== laravel/v10 rules === + +## Laravel 10 + +- Use the `search-docs` tool to get version-specific documentation. +- Middleware typically live in `app/Http/Middleware/` and service providers in `app/Providers/`. +- Laravel 10 has a `bootstrap/app.php` file that creates the application instance and binds kernel contracts, but does not use it for application configuration like Laravel 11: + - Middleware registration is in `app/Http/Kernel.php` + - Exception handling is in `app/Exceptions/Handler.php` + - Console commands and schedule registration is in `app/Console/Kernel.php` + - Rate limits likely exist in `RouteServiceProvider` or `app/Http/Kernel.php` +- When using Eloquent model casts, you must use `protected $casts = [];` and not the `casts()` method. The `casts()` method isn't available on models in Laravel 10. + +=== phpunit/core rules === + +## PHPUnit + +- This application uses PHPUnit for testing. All tests must be written as PHPUnit classes. Use `php artisan make:test --phpunit {name}` to create a new test. +- If you see a test using "Pest", convert it to PHPUnit. +- Every time a test has been updated, run that singular test. +- When the tests relating to your feature are passing, ask the user if they would like to also run the entire test suite to make sure everything is still passing. +- Tests should test all of the happy paths, failure paths, and weird paths. +- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files; these are core to the application. + +### Running Tests +- Run the minimal number of tests, using an appropriate filter, before finalizing. +- To run all tests: `php artisan test --compact`. +- To run all tests in a file: `php artisan test --compact tests/Feature/ExampleTest.php`. +- To filter on a particular test name: `php artisan test --compact --filter=testName` (recommended after making a change to a related file). + diff --git a/_ide_helper.php b/_ide_helper.php index eb861cb..ff24f30 100644 --- a/_ide_helper.php +++ b/_ide_helper.php @@ -256,7 +256,7 @@ } /** * Get the path to the views directory. - * + * * This method returns the first configured path in the array of view paths. * * @param string $path @@ -942,9 +942,9 @@ } /** * Returns true if the container can return an entry for the given identifier. - * + * * Returns false otherwise. - * + * * `has($id)` returning true does not mean that `get($id)` will not throw an exception. * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. * @@ -1963,7 +1963,7 @@ } /** * Log the user out of the application on their current device only. - * + * * This method does not cycle the "remember" token. * * @return void @@ -1976,7 +1976,7 @@ } /** * Invalidate other sessions for the current user. - * + * * The application must be using the AuthenticateSession middleware. * * @param string $password @@ -2909,7 +2909,7 @@ } /** * Dispatch a command to its appropriate handler in the current process. - * + * * Queueable jobs will be dispatched to the "sync" queue. * * @param mixed $command @@ -3474,7 +3474,7 @@ } /** * Retrieve multiple items from the cache by key. - * + * * Items not found in the cache will have a null value. * * @param array $keys @@ -6334,7 +6334,7 @@ } /** * Recursively delete a directory. - * + * * The directory itself may be optionally preserved. * * @param string $directory @@ -7327,7 +7327,7 @@ } /** * Action must be taken immediately. - * + * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * @@ -7343,7 +7343,7 @@ } /** * Critical conditions. - * + * * Example: Application component unavailable, unexpected exception. * * @param string $message @@ -7372,7 +7372,7 @@ } /** * Exceptional occurrences that are not errors. - * + * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * @@ -7401,7 +7401,7 @@ } /** * Interesting events. - * + * * Example: User logs in, SQL logs. * * @param string $message @@ -9250,7 +9250,7 @@ } /** * This method belongs to Symfony HttpFoundation and is not usually needed when using Laravel. - * + * * Instead, you may use the "input" method. * * @param string $key @@ -9509,7 +9509,7 @@ } /** * Sets the parameters for this request. - * + * * This method also re-initializes all properties. * * @param array $query The GET parameters @@ -9538,7 +9538,7 @@ } /** * Creates a Request based on a given URI and configuration. - * + * * The information contained in the URI always take precedence * over the other information (server and parameters). * @@ -9559,7 +9559,7 @@ } /** * Sets a callable able to create a Request instance. - * + * * This is mainly useful when you need to override the Request class * to keep BC with an existing system. It should not be used for any * other purpose. @@ -9572,7 +9572,7 @@ } /** * Overrides the PHP global variables according to this request instance. - * + * * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE. * $_FILES is never overridden, see rfc1867 * @@ -9585,7 +9585,7 @@ } /** * Sets a list of trusted proxies. - * + * * You should only list the reverse proxies that you manage directly. * * @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR'] @@ -9618,7 +9618,7 @@ } /** * Sets a list of trusted host patterns. - * + * * You should only list the hosts you manage using regexs. * * @param array $hostPatterns A list of trusted host patterns @@ -9640,7 +9640,7 @@ } /** * Normalizes a query string. - * + * * It builds a normalized query string, where keys/value pairs are alphabetized, * have consistent escaping and unneeded delimiters are removed. * @@ -9653,13 +9653,13 @@ } /** * Enables support for the _method request parameter to determine the intended HTTP method. - * + * * Be warned that enabling this feature might lead to CSRF issues in your code. * Check that you are using CSRF tokens when required. * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered * and used to send a "PUT" or "DELETE" request via the _method request parameter. * If these methods are not protected against CSRF, this presents a possible vulnerability. - * + * * The HTTP method can only be overridden when the real HTTP method is POST. * * @static @@ -9692,7 +9692,7 @@ } /** * Whether the request contains a Session object. - * + * * This method does not give any information about the state of the session object, * like whether the session is started or not. It is just a way to check if this Request * is associated with a Session instance. @@ -9726,11 +9726,11 @@ } /** * Returns the client IP addresses. - * + * * In the returned array the most trusted IP address is first, and the * least trusted one last. The "real" client IP address is the last one, * but this is also the least trusted one. Trusted proxies are stripped. - * + * * Use this method carefully; you should use getClientIp() instead. * * @return array @@ -9744,13 +9744,13 @@ } /** * Returns the client IP address. - * + * * This method can read the client IP address from the "X-Forwarded-For" header * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" * header value is a comma+space separated list of IP addresses, the left-most * being the original client, and each successive proxy that passed the request * adding the IP address where it received the request from. - * + * * If your reverse proxy uses a different header name than "X-Forwarded-For", * ("Client-Ip" for instance), configure it via the $trustedHeaderSet * argument of the Request::setTrustedProxies() method instead. @@ -9778,11 +9778,11 @@ } /** * Returns the path being requested relative to the executed script. - * + * * The path info always starts with a /. - * + * * Suppose this request is instantiated from /mysite on localhost: - * + * * * http://localhost/mysite returns an empty string * * http://localhost/mysite/about returns '/about' * * http://localhost/mysite/enco%20ded returns '/enco%20ded' @@ -9798,9 +9798,9 @@ } /** * Returns the root path from which this request is executed. - * + * * Suppose that an index.php file instantiates this request object: - * + * * * http://localhost/index.php returns an empty string * * http://localhost/index.php/page returns an empty string * * http://localhost/web/index.php returns '/web' @@ -9816,9 +9816,9 @@ } /** * Returns the root URL from which this request is executed. - * + * * The base URL never ends with a /. - * + * * This is similar to getBasePath(), except that it also includes the * script filename (e.g. index.php) if one exists. * @@ -9843,10 +9843,10 @@ } /** * Returns the port on which the request is made. - * + * * This method can read the client port from the "X-Forwarded-Port" header * when trusted proxies were set via "setTrustedProxies()". - * + * * The "X-Forwarded-Port" header must contain the client port. * * @return int|string|null Can be a string if fetched from the server bag @@ -9892,7 +9892,7 @@ } /** * Returns the HTTP host being requested. - * + * * The port name will be appended to the host if it's non-standard. * * @return string @@ -9916,7 +9916,7 @@ } /** * Gets the scheme and HTTP host. - * + * * If the URL was called with basic authentication, the user * and the password are not added to the generated string. * @@ -9954,12 +9954,12 @@ } /** * Returns the path as relative reference from the current Request path. - * + * * Only the URIs path component (no schema, host etc.) is relevant and must be given. * Both paths must be absolute and not contain relative parts. * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. * Furthermore, they can be used to reduce the link size in documents. - * + * * Example target paths, given a base path of "/a/b/c/d": * - "/a/b/c/d" -> "" * - "/a/b/c/" -> "./" @@ -9977,7 +9977,7 @@ } /** * Generates the normalized query string for the Request. - * + * * It builds a normalized query string, where keys/value pairs are alphabetized * and have consistent escaping. * @@ -9991,10 +9991,10 @@ } /** * Checks whether the request is secure or not. - * + * * This method can read the client protocol from the "X-Forwarded-Proto" header * when trusted proxies were set via "setTrustedProxies()". - * + * * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". * * @return bool @@ -10007,10 +10007,10 @@ } /** * Returns the host name. - * + * * This method can read the client host name from the "X-Forwarded-Host" header * when trusted proxies were set via "setTrustedProxies()". - * + * * The "X-Forwarded-Host" header must contain the client host name. * * @return string @@ -10034,13 +10034,13 @@ } /** * Gets the request "intended" method. - * + * * If the X-HTTP-Method-Override header is set, and if the method is a POST, * then it is used to determine the "real" intended HTTP method. - * + * * The _method request parameter can also be used to determine the HTTP method, * but only if enableHttpMethodParameterOverride() has been called. - * + * * The method is always an uppercased string. * * @return string @@ -10109,9 +10109,9 @@ } /** * Gets the request format. - * + * * Here is the process to determine the format: - * + * * * format defined by the user (with setRequestFormat()) * * _format request attribute * * $default @@ -10237,7 +10237,7 @@ } /** * Returns the protocol version. - * + * * If the application is behind a proxy, the protocol version used in the * requests between the client and the proxy and between the proxy and the * server might be different. This returns the former (from the "Via" header) @@ -10288,7 +10288,7 @@ * Gets the preferred format for the response by inspecting, in the following order: * * the request format set using setRequestFormat; * * the values of the Accept HTTP header. - * + * * Note that if you use this method, you should send the "Vary: Accept" header * in the response to prevent any issues with intermediary HTTP caches. * @@ -10357,7 +10357,7 @@ } /** * Returns true if the request is an XMLHttpRequest. - * + * * It works if your JavaScript library sets an X-Requested-With HTTP header. * It is known to work with common JavaScript frameworks: * @@ -10383,7 +10383,7 @@ } /** * Indicates whether this request originated from a trusted proxy. - * + * * This can be useful to determine whether or not to trust the * contents of a proxy-specific header. * @@ -10767,7 +10767,7 @@ } /** * Retrieve input as a boolean value. - * + * * Returns true when value is "1", "true", "on", and "yes". Otherwise, returns false. * * @param string|null $key @@ -11750,7 +11750,7 @@ } /** * Add a middleware to the beginning of a middleware group. - * + * * If the middleware is already in the group, it will not be added again. * * @param string $group @@ -11765,7 +11765,7 @@ } /** * Add a middleware to the end of a middleware group. - * + * * If the middleware is already in the group, it will not be added again. * * @param string $group @@ -15728,7 +15728,7 @@ } /** * This method belongs to Symfony HttpFoundation and is not usually needed when using Laravel. - * + * * Instead, you may use the "input" method. * * @param string $key @@ -15987,7 +15987,7 @@ } /** * Sets the parameters for this request. - * + * * This method also re-initializes all properties. * * @param array $query The GET parameters @@ -16016,7 +16016,7 @@ } /** * Creates a Request based on a given URI and configuration. - * + * * The information contained in the URI always take precedence * over the other information (server and parameters). * @@ -16037,7 +16037,7 @@ } /** * Sets a callable able to create a Request instance. - * + * * This is mainly useful when you need to override the Request class * to keep BC with an existing system. It should not be used for any * other purpose. @@ -16050,7 +16050,7 @@ } /** * Overrides the PHP global variables according to this request instance. - * + * * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE. * $_FILES is never overridden, see rfc1867 * @@ -16063,7 +16063,7 @@ } /** * Sets a list of trusted proxies. - * + * * You should only list the reverse proxies that you manage directly. * * @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR'] @@ -16096,7 +16096,7 @@ } /** * Sets a list of trusted host patterns. - * + * * You should only list the hosts you manage using regexs. * * @param array $hostPatterns A list of trusted host patterns @@ -16118,7 +16118,7 @@ } /** * Normalizes a query string. - * + * * It builds a normalized query string, where keys/value pairs are alphabetized, * have consistent escaping and unneeded delimiters are removed. * @@ -16131,13 +16131,13 @@ } /** * Enables support for the _method request parameter to determine the intended HTTP method. - * + * * Be warned that enabling this feature might lead to CSRF issues in your code. * Check that you are using CSRF tokens when required. * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered * and used to send a "PUT" or "DELETE" request via the _method request parameter. * If these methods are not protected against CSRF, this presents a possible vulnerability. - * + * * The HTTP method can only be overridden when the real HTTP method is POST. * * @static @@ -16170,7 +16170,7 @@ } /** * Whether the request contains a Session object. - * + * * This method does not give any information about the state of the session object, * like whether the session is started or not. It is just a way to check if this Request * is associated with a Session instance. @@ -16204,11 +16204,11 @@ } /** * Returns the client IP addresses. - * + * * In the returned array the most trusted IP address is first, and the * least trusted one last. The "real" client IP address is the last one, * but this is also the least trusted one. Trusted proxies are stripped. - * + * * Use this method carefully; you should use getClientIp() instead. * * @return array @@ -16222,13 +16222,13 @@ } /** * Returns the client IP address. - * + * * This method can read the client IP address from the "X-Forwarded-For" header * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" * header value is a comma+space separated list of IP addresses, the left-most * being the original client, and each successive proxy that passed the request * adding the IP address where it received the request from. - * + * * If your reverse proxy uses a different header name than "X-Forwarded-For", * ("Client-Ip" for instance), configure it via the $trustedHeaderSet * argument of the Request::setTrustedProxies() method instead. @@ -16256,11 +16256,11 @@ } /** * Returns the path being requested relative to the executed script. - * + * * The path info always starts with a /. - * + * * Suppose this request is instantiated from /mysite on localhost: - * + * * * http://localhost/mysite returns an empty string * * http://localhost/mysite/about returns '/about' * * http://localhost/mysite/enco%20ded returns '/enco%20ded' @@ -16276,9 +16276,9 @@ } /** * Returns the root path from which this request is executed. - * + * * Suppose that an index.php file instantiates this request object: - * + * * * http://localhost/index.php returns an empty string * * http://localhost/index.php/page returns an empty string * * http://localhost/web/index.php returns '/web' @@ -16294,9 +16294,9 @@ } /** * Returns the root URL from which this request is executed. - * + * * The base URL never ends with a /. - * + * * This is similar to getBasePath(), except that it also includes the * script filename (e.g. index.php) if one exists. * @@ -16321,10 +16321,10 @@ } /** * Returns the port on which the request is made. - * + * * This method can read the client port from the "X-Forwarded-Port" header * when trusted proxies were set via "setTrustedProxies()". - * + * * The "X-Forwarded-Port" header must contain the client port. * * @return int|string|null Can be a string if fetched from the server bag @@ -16370,7 +16370,7 @@ } /** * Returns the HTTP host being requested. - * + * * The port name will be appended to the host if it's non-standard. * * @return string @@ -16394,7 +16394,7 @@ } /** * Gets the scheme and HTTP host. - * + * * If the URL was called with basic authentication, the user * and the password are not added to the generated string. * @@ -16432,12 +16432,12 @@ } /** * Returns the path as relative reference from the current Request path. - * + * * Only the URIs path component (no schema, host etc.) is relevant and must be given. * Both paths must be absolute and not contain relative parts. * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. * Furthermore, they can be used to reduce the link size in documents. - * + * * Example target paths, given a base path of "/a/b/c/d": * - "/a/b/c/d" -> "" * - "/a/b/c/" -> "./" @@ -16455,7 +16455,7 @@ } /** * Generates the normalized query string for the Request. - * + * * It builds a normalized query string, where keys/value pairs are alphabetized * and have consistent escaping. * @@ -16469,10 +16469,10 @@ } /** * Checks whether the request is secure or not. - * + * * This method can read the client protocol from the "X-Forwarded-Proto" header * when trusted proxies were set via "setTrustedProxies()". - * + * * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". * * @return bool @@ -16485,10 +16485,10 @@ } /** * Returns the host name. - * + * * This method can read the client host name from the "X-Forwarded-Host" header * when trusted proxies were set via "setTrustedProxies()". - * + * * The "X-Forwarded-Host" header must contain the client host name. * * @return string @@ -16512,13 +16512,13 @@ } /** * Gets the request "intended" method. - * + * * If the X-HTTP-Method-Override header is set, and if the method is a POST, * then it is used to determine the "real" intended HTTP method. - * + * * The _method request parameter can also be used to determine the HTTP method, * but only if enableHttpMethodParameterOverride() has been called. - * + * * The method is always an uppercased string. * * @return string @@ -16587,9 +16587,9 @@ } /** * Gets the request format. - * + * * Here is the process to determine the format: - * + * * * format defined by the user (with setRequestFormat()) * * _format request attribute * * $default @@ -16715,7 +16715,7 @@ } /** * Returns the protocol version. - * + * * If the application is behind a proxy, the protocol version used in the * requests between the client and the proxy and between the proxy and the * server might be different. This returns the former (from the "Via" header) @@ -16766,7 +16766,7 @@ * Gets the preferred format for the response by inspecting, in the following order: * * the request format set using setRequestFormat; * * the values of the Accept HTTP header. - * + * * Note that if you use this method, you should send the "Vary: Accept" header * in the response to prevent any issues with intermediary HTTP caches. * @@ -16835,7 +16835,7 @@ } /** * Returns true if the request is an XMLHttpRequest. - * + * * It works if your JavaScript library sets an X-Requested-With HTTP header. * It is known to work with common JavaScript frameworks: * @@ -16861,7 +16861,7 @@ } /** * Indicates whether this request originated from a trusted proxy. - * + * * This can be useful to determine whether or not to trust the * contents of a proxy-specific header. * @@ -17245,7 +17245,7 @@ } /** * Retrieve input as a boolean value. - * + * * Returns true when value is "1", "true", "on", and "yes". Otherwise, returns false. * * @param string|null $key @@ -18754,7 +18754,7 @@ * A simple API extension for DateTime. * * @mixin DeprecatedProperties - * + * * * @property int $year * @property int $yearIso @@ -19235,7 +19235,7 @@ * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) * @method static static|false createFromFormat(string $format, string $time, DateTimeZone|string|false|null $timezone = null) Parse a string into a new Carbon object according to the specified format. * @method static static __set_state(array $array) https://php.net/manual/en/datetime.set-state.php - * + * * */ class Carbon { @@ -19538,7 +19538,7 @@ class DataTables { /** * Make a DataTable instance from source. - * + * * Alias of make for backward compatibility. * * @param mixed $source @@ -19812,9 +19812,9 @@ } /** * Output the PDF as a string. - * + * * The options parameter controls the output. Accepted options are: - * + * * 'compress' = > 1 or 0 - apply content stream compression, this is * on (1) by default * @@ -19983,9 +19983,9 @@ } /** * Output the PDF as a string. - * + * * The options parameter controls the output. Accepted options are: - * + * * 'compress' = > 1 or 0 - apply content stream compression, this is * on (1) by default * @@ -20278,7 +20278,7 @@ } /** * Adds a message to the MessagesCollector - * + * * A message can be anything from an object to a string * * @param mixed $message @@ -20404,7 +20404,7 @@ } /** * Returns the HTTP driver - * + * * If no http driver where defined, a PhpHttpDriver is automatically created * * @return \DebugBar\HttpDriverInterface @@ -20417,7 +20417,7 @@ } /** * Returns collected data - * + * * Will collect the data if none have been collected yet * * @return array @@ -20833,7 +20833,7 @@ /** * Allows to charge for additional fees that may or may not be taxable * ex - service fee , delivery fee, tips. - * + * * Because it uses ->put, the name must be unique otherwise will be overwritten. * * @param $name diff --git a/_ide_helper_models.php b/_ide_helper_models.php index 7ea5f1d..966c260 100644 --- a/_ide_helper_models.php +++ b/_ide_helper_models.php @@ -11,3 +11,5223 @@ */ +namespace App\Models{ +/** + * App\Models\Account + * + * @property-read \App\Models\Country $company_country + * @property-read \App\Models\Country $company_pre_phone + * @property-read \App\Models\Country $country + * @property-read mixed $company + * @property-read \App\Models\Country $pre_mobil + * @property-read \App\Models\Country $pre_phone + * @property-read \App\User $user + * @method static bool|null forceDelete() + * @method static \Illuminate\Database\Query\Builder|\App\Models\Account onlyTrashed() + * @method static bool|null restore() + * @method static \Illuminate\Database\Query\Builder|\App\Models\Account withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\Models\Account withoutTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Account newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Account newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Account query() + * @mixin \Eloquent + */ + class Account extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Airline + * + * @property int $id + * @property string $name + * @property string $name_full + * @property Carbon $created_at + * @property Carbon $updated_at + * @package App\Models + * @property array|null $contact_emails + * @property array|null $emails + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline whereContactEmails($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline whereEmails($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline whereNameFull($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Airline whereUpdatedAt($value) + * @property string|null $flight_info + * @property string|null $check_in + * @property string|null $baggage + * @method static \Illuminate\Database\Eloquent\Builder|Airline whereBaggage($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airline whereCheckIn($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airline whereFlightInfo($value) + * @mixin \Eloquent + */ + class Airline extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Airport + * + * @property int $id + * @property string $code + * @property string $name + * @property string $city + * @property string $country + * @property bool $active + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|Airport newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Airport newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Airport query() + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereCity($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereCountry($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Airport whereUpdatedAt($value) + * @mixin \Eloquent + */ + class Airport extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class AnswerQuestion + * + * @property int $id + * @property string $question + * @property string $question_text + * @property string $answer + * @property string $answer_text + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereAnswer($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereAnswerText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereQuestion($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereQuestionText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereUpdatedAt($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentFaq[] $iq_content_faq + * @property-read int|null $iq_content_faq_count + * @property int|null $i_q_content_category_id + * @property-read \App\Models\IQContentCategory|null $iq_content_category + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\AnswerQuestion whereIQContentCategoryId($value) + * @mixin \Eloquent + */ + class AnswerQuestion extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Arrangement + * + * @property int $id + * @property int $template_id + * @property Carbon $state + * @property Carbon $begin + * @property Carbon $end + * @property string $type_s + * @property string $data_s + * @property int $view_position + * @property int $booking_id + * @property int $type_id + * @property bool $in_pdf + * @property Booking $booking + * @property ArrangementTemplate $arrangement_template + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereBegin($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereDataS($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereEnd($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereInPdf($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereState($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereTemplateId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereTypeS($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Arrangement whereViewPosition($value) + * @property-read \App\Models\ArrangementType|null $arrangement_type + * @mixin \Eloquent + */ + class Arrangement extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class ArrangementTemplate + * + * @property int $id + * @property string $title + * @property Collection|Arrangement[] $arrangements + * @package App\Models + * @property-read int|null $arrangements_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementTemplate newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementTemplate newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementTemplate query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementTemplate whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementTemplate whereTitle($value) + * @mixin \Eloquent + */ + class ArrangementTemplate extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class ArrangementType + * + * @property int $id + * @property string $name + * @property int $notify_rel_days + * @property int $arrangement_type_id + * @property bool $is_unique + * @property int $notify_rel_field_key + * @property ArrangementType $arrangement_type + * @property Booking $booking + * @property DraftItem $draft_item + * @property DraftType $draft_type + * @property Collection|ArrangementType[] $arrangement_types + * @property Collection|InquiryType[] $inquiry_types + * @package App\Models + * @property-read int|null $arrangement_types_count + * @property-read int|null $inquiry_types_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType whereArrangementTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType whereIsUnique($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType whereNotifyRelDays($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ArrangementType whereNotifyRelFieldKey($value) + * @mixin \Eloquent + */ + class ArrangementType extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Booking + * + * @property int $id + * @property Carbon $booking_date + * @property int $customer_id + * @property int $lead_id + * @property bool $new_drafts + * @property int $sf_guard_user_id + * @property int $branch_id + * @property float $service_fee + * @property int $travel_country_id + * @property int $travel_category_id + * @property int $pax + * @property int $coupon_id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $title + * @property Carbon $start_date + * @property Carbon $end_date + * @property int $website_id + * @property string $travel_number + * @property string $participant_name + * @property string $participant_firstname + * @property Carbon $participant_birthdate + * @property int $participant_salutation_id + * @property string $ev_number + * @property string $merlin_knr + * @property string $merlin_order_number + * @property int $travel_company_id + * @property bool $travel_documents + * @property float $price + * @property float $price_total + * @property float $deposit_total + * @property float $final_payment + * @property Carbon $final_payment_date + * @property int $travelagenda_id + * @property Branch $branch + * @property Coupon $coupon + * @property Customer $customer + * @property Lead $lead + * @property SfGuardUser $sf_guard_user + * @property TravelCategory $travel_category + * @property TravelCompany $travel_company + * @property TravelCountry $travel_country + * @property TravelAgenda $travel_agenda + * @property Collection|Arrangement[] $arrangements + * @property Collection|ArrangementType[] $arrangement_types + * @property Collection|BookingApplication[] $booking_applications + * @property Collection|BookingConfirmation[] $booking_confirmations + * @property Collection|BookingDraftItem[] $booking_draft_items + * @property Collection|BookingInvoice[] $booking_invoices + * @property Collection|BookingServiceItem[] $booking_service_items + * @property Collection|BookingVoucher[] $booking_vouchers + * @property Collection|BookingVoucherAgency[] $booking_voucher_agencys + * @property Collection|Coupon[] $coupons + * @property Collection|InsuranceCertificate[] $insurance_certificates + * @property Collection|Participant[] $participants + * @property Collection|ServiceProviderEntry[] $service_provider_entries + * @property Collection|TravelInsurance[] $travel_insurances + * @package App\Models + * @property-read int|null $arrangement_types_count + * @property-read int|null $arrangements_count + * @property-read int|null $booking_draft_items_count + * @property-read int|null $booking_service_items_count + * @property-read int|null $coupons_count + * @property-read int|null $insurance_certificates_count + * @property-read int|null $participants_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereBookingDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereBranchId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereCouponId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereDepositTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereEndDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereEvNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereFinalPayment($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereFinalPaymentDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereMerlinKnr($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereMerlinOrderNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereNewDrafts($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereParticipantBirthdate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereParticipantFirstname($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereParticipantName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereParticipantSalutationId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking wherePax($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking wherePrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking wherePriceTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereServiceFee($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereSfGuardUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereStartDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereTravelCategoryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereTravelCompanyId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereTravelDocuments($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereTravelNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereTravelagendaId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereWebsiteId($value) + * @property-read int|null $service_provider_entries_count + * @property float|null $canceled + * @property float|null $price_canceled + * @property int|null $paying_out + * @property int|null $paying_out_status + * @property int|null $airline_id + * @property int|null $refund + * @property \Illuminate\Support\Carbon|null $refund_date + * @property int|null $hold + * @property int|null $xx_tkt + * @property string|null $xx_tkt_date + * @property string|null $filekey + * @property-read \App\Models\Airline|null $airline + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\CustomerMail[] $customer_mails + * @property-read int|null $customer_mails_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\CustomerMail[] $customer_mails_sent_at + * @property-read int|null $customer_mails_sent_at_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereAirlineId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereCanceled($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereFilekey($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereHold($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking wherePayingOut($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking wherePayingOutStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking wherePriceCanceled($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereRefund($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereRefundDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereXxTkt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereXxTktDate($value) + * @property int|null $is_rail_fly + * @property string|null $notice + * @property-read \App\Models\CustomerMail $customer_mail_last + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereIsRailFly($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereNotice($value) + * @property-read int|null $booking_applications_count + * @property-read int|null $booking_confirmations_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingStorno[] $booking_stornos + * @property-read int|null $booking_stornos_count + * @property-read int|null $booking_vouchers_count + * @property-read int|null $booking_voucher_agencys_count + * @property-read int|null $travel_insurances_count + * @property string|null $origin_start_date + * @property \Illuminate\Support\Carbon|null $lawyer_date + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereLawyerDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereOriginStartDate($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingFile[] $booking_files + * @property-read int|null $booking_files_count + * @property float|null $price_balance + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking wherePriceBalance($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingCountryService[] $booking_country_services + * @property-read int|null $booking_country_services_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingCountryService[] $booking_country_services_checked + * @property-read int|null $booking_country_services_checked_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingCompanyService[] $booking_company_services + * @property-read int|null $booking_company_services_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingCompanyService[] $booking_company_services_checked + * @property-read int|null $booking_company_services_checked_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingProviderService[] $booking_provider_services + * @property-read int|null $booking_provider_services_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingProviderService[] $booking_provider_services_checked + * @property-read int|null $booking_provider_services_checked_count + * @property bool|null $comfort + * @property int|null $nationality_id + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BookingNotice[] $booking_notices + * @property-read int|null $booking_notices_count + * @property-read \App\Models\TravelNationality|null $travel_nationality + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereComfort($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Booking whereNationalityId($value) + * @property bool|null $participant_pass + * @property array|null $airline_ids + * @property-read \App\Models\BookingStorno|null $booking_strono + * @method static \Illuminate\Database\Eloquent\Builder|Booking whereAirlineIds($value) + * @method static \Illuminate\Database\Eloquent\Builder|Booking whereParticipantPass($value) + * @property int|null $insurance_offer + * @property int|null $airport_id + * @property-read \App\Models\Airport|null $airport + * @property-read Collection $booking_documents + * @property-read int|null $booking_documents_count + * @property-read Collection $customer_mails_reverse + * @property-read int|null $customer_mails_reverse_count + * @property-read \App\Models\Salutation|null $participant_salutation + * @method static \Illuminate\Database\Eloquent\Builder|Booking whereAirportId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Booking whereInsuranceOffer($value) + * @mixin \Eloquent + */ + class Booking extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingApplication + * + * @property int $id + * @property int $booking_id + * @property float $total + * @property boolean $binary_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication whereTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingApplication whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingApplication extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingCompanyService + * + * @property int $id + * @property int $travel_company_service_id + * @property int $booking_id + * @property int $status + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @property TravelCompanyService $travel_company_service + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService whereTravelCompanyServiceId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCompanyService whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingCompanyService extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingConfirmation + * + * @property int $id + * @property int $booking_id + * @property float $total + * @property float $deposit + * @property float $final_payment + * @property Carbon $deposit_payment_date + * @property Carbon $final_payment_date + * @property boolean $binary_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereDeposit($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereDepositPaymentDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereFinalPayment($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereFinalPaymentDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingConfirmation whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingConfirmation extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingCountryService + * + * @property int $id + * @property int $travel_country_service_id + * @property int $booking_id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @property TravelCountryService $travel_country_service + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService whereTravelCountryServiceId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService whereUpdatedAt($value) + * @property int|null $status + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService whereStatus($value) + * @mixin \Eloquent + */ + class BookingCountryService extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingDocument + * + * @property int $id + * @property int|null $booking_id + * @property int|null $customer_id + * @property int|null $lead_id + * @property string $identifier + * @property string $filename + * @property string $dir + * @property string $original_name + * @property string $ext + * @property string $mine + * @property int $size + * @property Carbon $date + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property Booking|null $booking + * @property Customer|null $customer + * @property Lead|null $lead + * @package App\Models + * @property \App\Models\Coupon|null $coupon_id + * @property int|null $booking_storno_id + * @property object|null $data + * @property int|null $status + * @property-read \App\Models\BookingStorno|null $booking_storno + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument query() + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereBookingStornoId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereCouponId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereData($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereOriginalName($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|BookingDocument whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingDocument extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\BookingDraftItem + * + * @property int $id + * @property int $booking_id + * @property int $travel_program_id + * @property int|null $travel_class_id + * @property int|null $draft_item_id + * @property int|null $draft_type_id + * @property string|null $request_date + * @property int|null $days_start + * @property int|null $days_duration + * @property string|null $start_date + * @property string|null $end_date + * @property string|null $service + * @property float|null $price_adult + * @property int|null $adult + * @property float|null $price_children + * @property int|null $children + * @property int|null $pos + * @property int $in_pdf + * @property int $status + * @property int $comfort + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\DraftItem|null $draft_item + * @property-read \App\Models\DraftType|null $draft_type + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereAdult($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereComfort($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereDaysDuration($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereDaysStart($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereDraftItemId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereDraftTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereEndDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereInPdf($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem wherePriceAdult($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem wherePriceChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereRequestDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereService($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereStartDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereTravelClassId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereTravelProgramId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereUpdatedAt($value) + * @property int|null $fewo_lodging_id + * @property float|null $price + * @property-read \App\Models\Booking $booking + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem whereFewoLodgingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem wherePrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingDraftItem query() + * @mixin \Eloquent + */ + class BookingDraftItem extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingFile + * + * @property int $id + * @property int $booking_id + * @property string $identifier + * @property string $filename + * @property string $dir + * @property string $original_name + * @property string $ext + * @property string $mine + * @property int $size + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereOriginalName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingFile whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingFile extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingInvoice + * + * @property int $id + * @property int $booking_id + * @property float $total + * @property float $deposit + * @property float $final_payment + * @property Carbon $deposit_payment_date + * @property Carbon $final_payment_date + * @property boolean $binary_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereDeposit($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereDepositPaymentDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereFinalPayment($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereFinalPaymentDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingInvoice whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingInvoice extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingNotice + * + * @property int $id + * @property int $booking_id + * @property int $from_user_id + * @property int $to_user_id + * @property string $message + * @property bool $show + * @property bool $important + * @property Carbon $edit_at + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @property User $user + * @package App\Models + * @property-read \App\User $from_user + * @property-read \App\User|null $to_user + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereEditAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereFromUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereImportant($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereShow($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereToUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingNotice whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingNotice extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingProviderService + * + * @property int $id + * @property int $service_provider_service_id + * @property int $booking_id + * @property int $status + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @property ServiceProviderService $service_provider_service + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService whereServiceProviderServiceId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingProviderService whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingProviderService extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingServiceItem + * + * @property int $id + * @property int $booking_id + * @property int $travel_company_id + * @property float $service_price + * @property float $service_price_refund + * @property float $commission + * @property Carbon $travel_date + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $name + * @property bool $is_commission_locked + * @property Booking $booking + * @property TravelCompany $travel_company + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereCommission($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereIsCommissionLocked($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereServicePrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereServicePriceRefund($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereTravelCompanyId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereTravelDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingServiceItem whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingServiceItem extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingStorno + * + * @property int $id + * @property int $booking_id + * @property float $total + * @property float $storno + * @property Carbon $storno_date + * @property boolean $binary_data + * @property bool $done + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereDone($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereStorno($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereStornoDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereUpdatedAt($value) + * @property \Illuminate\Support\Carbon|null $storno_print + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingStorno whereStornoPrint($value) + * @property-read \App\Models\BookingDocument|null $booking_document + * @mixin \Eloquent + */ + class BookingStorno extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingVoucher + * + * @property int $id + * @property int $booking_id + * @property boolean $binary_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucher whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingVoucher extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class BookingVoucherAgency + * + * @property int $id + * @property int $booking_id + * @property boolean $binary_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingVoucherAgency whereUpdatedAt($value) + * @mixin \Eloquent + */ + class BookingVoucherAgency extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Branch + * + * @property int $id + * @property string $name + * @property Collection|Booking[] $bookings + * @property Collection|SfGuardUser[] $sf_guard_users + * @package App\Models + * @property-read int|null $bookings_count + * @property-read int|null $sf_guard_users_count + * @method static \Illuminate\Database\Eloquent\Builder|Branch newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Branch newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Branch query() + * @method static \Illuminate\Database\Eloquent\Builder|Branch whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Branch whereName($value) + * @mixin \Eloquent + */ + class Branch extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class CMSAuthor + * + * @property int $id + * @property string $name + * @property Carbon $created_at + * @property Carbon $updated_at + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor whereUpdatedAt($value) + * @property string|null $description + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor whereDescription($value) + * @mixin \Eloquent + */ + class CMSAuthor extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\CMSContent + * + * @property int $id + * @property string $name + * @property string $slug + * @property string|null $identifier + * @property string $field + * @property string|null $text + * @property string|null $full_text + * @property array|null $object + * @property int|null $integer + * @property string|null $decimal + * @property int|null $pos + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent findSimilarSlugs(string $attribute, array $config, string $slug) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent query() + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereDecimal($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereField($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereFullText($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereInteger($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereObject($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereText($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSContent withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class CMSContent extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\CMSInfo + * + * @property int $id + * @property string $name + * @property string $slug + * @property string $field + * @property string|null $text + * @property string|null $full_text + * @property int|null $integer + * @property float|null $decimal + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo findSimilarSlugs($attribute, $config, $slug) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereDecimal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereField($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereFullText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereInteger($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereUpdatedAt($value) + * @property string $type + * @property int $bool + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereBool($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfo whereType($value) + * @method static \Illuminate\Database\Eloquent\Builder|CMSInfo withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class CMSInfo extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\CMSInfoAvailable + * + * @property int $id + * @property string $type + * @property bool $active + * @property string|null $from + * @property string|null $to + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereFrom($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereTo($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereType($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereUpdatedAt($value) + * @property int $wday + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereWday($value) + * @property int|null $special + * @property string|null $date + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoAvailable whereSpecial($value) + * @mixin \Eloquent + */ + class CMSInfoAvailable extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\CMSInfoHoliday + * + * @property int $id + * @property bool $local + * @property bool $phone + * @property string $from + * @property string $to + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday whereFrom($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday whereLocal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday wherePhone($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday whereTo($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSInfoHoliday whereUpdatedAt($value) + * @mixin \Eloquent + */ + class CMSInfoHoliday extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\Country + * + * @property int $id + * @property string $code + * @property string $phone + * @property string $en + * @property string $de + * @property string $es + * @property string $fr + * @property string $it + * @property string $ru + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereDe($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereEn($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereEs($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereFr($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereIt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country wherePhone($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country whereRu($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Country query() + * @mixin \Eloquent + */ + class Country extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Coupon + * + * @property int $id + * @property string $number + * @property int $customer_id + * @property int $booking_id + * @property float $value + * @property Carbon $issue_date + * @property Carbon $valid_date + * @property bool $is_redeemed + * @property Carbon $redeem_date + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @property Customer $customer + * @property Collection|Booking[] $bookings + * @package App\Models + * @property-read int|null $bookings_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereIsRedeemed($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereIssueDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereRedeemDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereValidDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereValue($value) + * @property string|null $text + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Coupon whereText($value) + * @property-read \App\Models\BookingDocument|null $booking_document + * @mixin \Eloquent + */ + class Coupon extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class CreditCardType + * + * @property int $id + * @property string $name + * @property Collection|Customer[] $customers + * @package App\Models + * @property-read int|null $customers_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CreditCardType newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CreditCardType newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CreditCardType query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CreditCardType whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CreditCardType whereName($value) + * @mixin \Eloquent + */ + class CreditCardType extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Customer + * + * @property int $id + * @property int $salutation_id + * @property string $title + * @property string $name + * @property string $firstname + * @property Carbon $birthdate + * @property string $company + * @property string $street + * @property string $zip + * @property string $city + * @property string $email + * @property string $phone + * @property string $phonebusiness + * @property string $phonemobile + * @property string $fax + * @property string $bank + * @property string $bank_code + * @property string $bank_account_number + * @property int $credit_card_type_id + * @property string $credit_card_number + * @property Carbon $credit_card_expiration_date + * @property string $participants_remarks + * @property string $miscellaneous_remarks + * @property Carbon $created_at + * @property Carbon $updated_at + * @property int $country_id + * @property TravelCountry $travel_country + * @property CreditCardType $credit_card_type + * @property Salutation $salutation + * @property Collection|Booking[] $bookings + * @property Collection|Coupon[] $coupons + * @property Collection|Lead[] $leads + * @package App\Models + * @property-read int|null $bookings_count + * @property-read int|null $coupons_count + * @property-read int|null $leads_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereBank($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereBankAccountNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereBankCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereBirthdate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereCity($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereCompany($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereCreditCardExpirationDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereCreditCardNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereCreditCardTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereFax($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereFirstname($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereMiscellaneousRemarks($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereParticipantsRemarks($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer wherePhone($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer wherePhonebusiness($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer wherePhonemobile($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereSalutationId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereStreet($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Customer whereZip($value) + * @mixin \Eloquent + */ + class Customer extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class CustomerFewoFile + * + * @property int $id + * @property int $travel_user_id + * @property int $customer_fewo_mail_id + * @property string $identifier + * @property string $filename + * @property string $dir + * @property string $original_name + * @property string $ext + * @property string $mine + * @property int $size + * @property Carbon $created_at + * @property Carbon $updated_at + * @property CustomerFewoMail $customer_fewo_mail + * @property TravelUser $travel_user + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereCustomerFewoMailId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereOriginalName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereTravelUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereUpdatedAt($value) + * @property \Illuminate\Support\Carbon|null $deleted_at + * @method static \Illuminate\Database\Query\Builder|\App\Models\CustomerFewoFile onlyTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoFile whereDeletedAt($value) + * @method static \Illuminate\Database\Query\Builder|\App\Models\CustomerFewoFile withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\Models\CustomerFewoFile withoutTrashed() + * @mixin \Eloquent + */ + class CustomerFewoFile extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class CustomerFewoMail + * + * @property int $id + * @property int $travel_user_booking_fewo_id + * @property int $travel_user_id + * @property bool $is_answer + * @property int $reply_id + * @property string $email + * @property string $recipient + * @property string $cc + * @property string $bcc + * @property string $subject + * @property string $message + * @property int $dir + * @property int $subdir + * @property bool $draft + * @property bool $important + * @property bool $send + * @property bool $fail + * @property string $error + * @property Carbon $sent_at + * @property Carbon $scheduled_at + * @property Carbon $delivered_at + * @property Carbon $created_at + * @property Carbon $updated_at + * @property CustomerFewoMail $customer_fewo_mail + * @property TravelUserBookingFewo $travel_user_booking_fewo + * @property TravelUser $travel_user + * @property Collection|CustomerFewoFile[] $customer_fewo_files + * @property Collection|CustomerFewoMail[] $customer_fewo_mails + * @package App\Models + * @property-read int|null $customer_fewo_files_count + * @property-read int|null $customer_fewo_mails_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereBcc($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereCc($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereDeliveredAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereDraft($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereError($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereFail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereImportant($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereIsAnswer($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereRecipient($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereReplyId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereScheduledAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereSend($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereSentAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereSubdir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereSubject($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereTravelUserBookingFewoId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereTravelUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereUpdatedAt($value) + * @property-read \App\Models\TravelUserBookingFewo $booking + * @property-read \App\Models\TravelUser $customer + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\CustomerFewoFile[] $customer_files + * @property-read int|null $customer_files_count + * @property-read \App\Models\CustomerFewoMail|null $customer_mail + * @property \Illuminate\Support\Carbon|null $deleted_at + * @method static \Illuminate\Database\Query\Builder|\App\Models\CustomerFewoMail onlyTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereDeletedAt($value) + * @method static \Illuminate\Database\Query\Builder|\App\Models\CustomerFewoMail withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\Models\CustomerFewoMail withoutTrashed() + * @property array|null $forward + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFewoMail whereForward($value) + * @mixin \Eloquent + */ + class CustomerFewoMail extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class CustomerFile + * + * @property int $id + * @property int $customer_id + * @property int $customer_mail_id + * @property string $identifier + * @property string $filename + * @property string $dir + * @property string $original_name + * @property string $ext + * @property string $mine + * @property int $size + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Customer $customer + * @property CustomerMail $customer_mail + * @package App\Models + * @property-read \App\User $user + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereCustomerMailId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereOriginalName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerFile whereUpdatedAt($value) + * @mixin \Eloquent + */ + class CustomerFile extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class CustomerMail + * + * @property int $id + * @property int $booking_id + * @property int $customer_id + * @property int $lead_id + * @property bool $is_answer + * @property int $reply_id + * @property string $email + * @property string $recipient + * @property string $cc + * @property string $bcc + * @property string $subject + * @property string $message + * @property int $dir + * @property int $travel_country_id + * @property bool $draft + * @property bool $important + * @property bool $send + * @property bool $fail + * @property string $error + * @property Carbon $sent_at + * @property Carbon $scheduled_at + * @property Carbon $delivered_at + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @property Customer $customer + * @property CustomerMail $customer_mail + * @property TravelCountry $travel_country + * @property Lead $lead + * @property Collection|CustomerFile[] $customer_files + * @property Collection|CustomerMail[] $customer_mails + * @package App\Models + * @property-read int|null $customer_files_count + * @property-read int|null $customer_mails_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereBcc($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereCc($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereDeliveredAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereDraft($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereError($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereFail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereImportant($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereIsAnswer($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereRecipient($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereReplyId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereScheduledAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereSend($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereSentAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereSubject($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereUpdatedAt($value) + * @property int|null $subdir + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereSubdir($value) + * @property array|null $forward + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CustomerMail whereForward($value) + * @mixin \Eloquent + */ + class CustomerMail extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\Draft + * + * @property int $id + * @property string $name + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft whereUpdatedAt($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\DraftItem[] $draft_items + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelProgramDraft[] $travel_program_drafts + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Draft query() + * @property-read int|null $draft_items_count + * @property-read int|null $travel_program_drafts_count + * @mixin \Eloquent + */ + class Draft extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\DraftItem + * + * @property int $id + * @property int $draft_id + * @property int $draft_type_id + * @property int|null $days_start + * @property int|null $days_duration + * @property string|null $name + * @property float|null $price_adult + * @property int|null $adult + * @property float|null $price_children + * @property int|null $children + * @property string|null $content + * @property int|null $pos + * @property int $in_pdf + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\Draft $draft + * @property-read \App\Models\DraftType $draft_type + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereAdult($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereContent($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereDaysDuration($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereDaysStart($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereDraftId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereDraftTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereInPdf($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem wherePriceAdult($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem wherePriceChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereUpdatedAt($value) + * @property string|null $service + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem whereService($value) + * @property float|null $price + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem wherePrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftItem query() + * @mixin \Eloquent + */ + class DraftItem extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\DraftType + * + * @property int $id + * @property string $name + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType whereUpdatedAt($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\DraftItem[] $draft_items + * @property string|null $color + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType whereColor($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType query() + * @property int|null $pos + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\DraftType wherePos($value) + * @property-read int|null $draft_items_count + * @mixin \Eloquent + */ + class DraftType extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class EmailTemplate + * + * @property int $id + * @property int $email_template_dir_id + * @property string $subject + * @property string $message + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property EmailTemplateDir $email_template_dir + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereEmailTemplateDirId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereSubject($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereUpdatedAt($value) + * @property string $name + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplate whereName($value) + * @mixin \Eloquent + */ + class EmailTemplate extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class EmailTemplateDir + * + * @property int $id + * @property string $name + * @property string $color + * @property bool $active + * @property int $pos + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Collection|EmailTemplate[] $email_templates + * @package App\Models + * @property-read int|null $email_templates_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir whereColor($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\EmailTemplateDir whereUpdatedAt($value) + * @mixin \Eloquent + */ + class EmailTemplateDir extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\Feedback + * + * @property int $id + * @property int|null $owner + * @property string|null $model + * @property int|null $lvl + * @property int $owner_second + * @property int|null $catalog_id + * @property int|null $catalog_index + * @property string|null $slug + * @property int|null $travel_program + * @property int|null $status + * @property int|null $show_in_navi + * @property int|null $order + * @property string|null $title + * @property string|null $pagetitle + * @property string|null $description + * @property string|null $keywords + * @property string|null $content + * @property string|null $content_new + * @property string|null $buma_destination + * @property int|null $OLD_CatalogID + * @property int|null $OLD_OwnerID + * @property int|null $buma_gjr + * @property string|null $date + * @property int $price-tags + * @property string|null $text_right + * @property string|null $keyword + * @property string|null $canonical_url + * @property int|null $country_id + * @property string|null $template + * @property int|null $lft + * @property int|null $rgt + * @property int|null $tree_root + * @property int|null $parent_id + * @property string|null $real_url_path + * @property string|null $box_body + * @property string|null $box_image_url + * @property string|null $box_star + * @property string|null $box_discount + * @property string|null $cms_settings + * @property int|null $fewo_lodging + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Feedback[] $children + * @property-read \App\Models\Feedback|null $parent + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereBoxBody($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereBoxDiscount($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereBoxImageUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereBoxStar($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereBumaDestination($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereBumaGjr($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereCanonicalUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereCatalogId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereCatalogIndex($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereCmsSettings($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereContent($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereContentNew($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereFewoLodging($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereKeyword($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereKeywords($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereLft($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereLvl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereModel($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereOLDCatalogID($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereOLDOwnerID($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereOrder($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereOwner($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereOwnerSecond($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback wherePagetitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereParentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback wherePriceTags($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereRealUrlPath($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereRgt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereShowInNavi($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereTemplate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereTextRight($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereTravelProgram($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereTreeRoot($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereUpdatedAt($value) + * @property int|null $travel_guide_content_id + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereTravelGuideContentId($value) + * @property string|null $title_short + * @property string|null $before_title + * @property-read int|null $children_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereBeforeTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feedback whereTitleShort($value) + * @property-read \Illuminate\Database\Eloquent\Collection $child_pages + * @property-read int|null $child_pages_count + * @property-read \App\Models\Page|null $page + * @property-read \Illuminate\Database\Eloquent\Collection $pages + * @property-read int|null $pages_count + * @property-read \App\Models\Page|null $parent_page + * @property-read \Illuminate\Database\Eloquent\Collection $travel_countries + * @property-read int|null $travel_countries_count + * @property-read \App\Models\TravelCountry|null $travel_country + * @property-read \App\Models\TravelGuide|null $travel_guide + * @property-read \App\Models\TravelProgram|null $travel_program_content + * @method static \Illuminate\Database\Eloquent\Builder|Page findSimilarSlugs(string $attribute, array $config, string $slug) + * @method static \Illuminate\Database\Eloquent\Builder|Page withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class Feedback extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoLodging + * + * @property int $id + * @property int $group_id + * @property int $type_id + * @property string $name + * @property string $description + * @property string $equipment + * @property string $adress1 + * @property string $adress2 + * @property string $zipcode + * @property string $city + * @property int $maximum_persons + * @property float $deposit + * @property bool $calendar_visible + * @property \App\Models\FewoLodgingType $fewo_lodging_type + * @property \App\Models\FewoLodgingGroup $fewo_lodging_group + * @property \Illuminate\Database\Eloquent\Collection $fewo_lodging_images + * @property \Illuminate\Database\Eloquent\Collection $fewo_prices + * @property \Illuminate\Database\Eloquent\Collection $fewo_reservations + * @property \Illuminate\Database\Eloquent\Collection $pages + * @property \Illuminate\Database\Eloquent\Collection $travel_user_booking_fewos + * @package App\Models + * @property string|null $single_name + * @property string $zip_code + * @property int|null $maximum_adults + * @property int|null $maximum_childs + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereAdress1($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereAdress2($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereCalendarVisible($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereCity($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereDeposit($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereEquipment($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereGroupId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereMaximumAdults($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereMaximumChilds($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereMaximumPersons($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereSingleName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging whereZipCode($value) + * @property-read int|null $fewo_lodging_images_count + * @property-read int|null $fewo_prices_count + * @property-read int|null $fewo_reservations_count + * @property-read int|null $pages_count + * @property-read int|null $travel_user_booking_fewos_count + * @property string|null $pdf_name + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodging wherePdfName($value) + * @mixin \Eloquent + */ + class FewoLodging extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoLodgingGroup + * + * @property int $id + * @property string $name + * @property \Illuminate\Database\Eloquent\Collection $fewo_lodgings + * @property \Illuminate\Database\Eloquent\Collection $fewo_lodging_group_images + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroup newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroup newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroup query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroup whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroup whereName($value) + * @property-read int|null $fewo_lodging_group_images_count + * @property-read int|null $fewo_lodgings_count + * @mixin \Eloquent + */ + class FewoLodgingGroup extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoLodgingGroupImage + * + * @property int $id + * @property int $group_id + * @property string $comp + * @property int $pos + * @property string $full_file_name + * @property string $file_name + * @property string $description + * @property \App\Models\FewoLodgingGroup $fewo_lodging_group + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage whereComp($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage whereFileName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage whereFullFileName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage whereGroupId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingGroupImage wherePos($value) + * @mixin \Eloquent + */ + class FewoLodgingGroupImage extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoLodgingImage + * + * @property int $id + * @property int $lodging_id + * @property int $pos + * @property string $full_file_name + * @property string $file_name + * @property string $description + * @property \App\Models\FewoLodging $fewo_lodging + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage whereFileName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage whereFullFileName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage whereLodgingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingImage wherePos($value) + * @mixin \Eloquent + */ + class FewoLodgingImage extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoLodgingType + * + * @property int $id + * @property string $name + * @property \Illuminate\Database\Eloquent\Collection $fewo_lodgings + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingType newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingType newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingType query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingType whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoLodgingType whereName($value) + * @property-read int|null $fewo_lodgings_count + * @mixin \Eloquent + */ + class FewoLodgingType extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoPrice + * + * @property int $id + * @property int $lodging_id + * @property int $season_id + * @property float $per_night + * @property float $flat_price + * @property \App\Models\FewoSeason $fewo_season + * @property \App\Models\FewoLodging $fewo_lodging + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice whereFlatPrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice whereLodgingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice wherePerNight($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoPrice whereSeasonId($value) + * @mixin \Eloquent + */ + class FewoPrice extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoReservation + * + * @property int $id + * @property int $lodging_id + * @property \Carbon\Carbon $from_date + * @property \Carbon\Carbon $to_date + * @property int $status + * @property int $type + * @property \App\Models\FewoLodging $fewo_lodging + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation whereFromDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation whereLodgingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation whereToDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoReservation whereType($value) + * @mixin \Eloquent + */ + class FewoReservation extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class FewoSeason + * + * @property int $id + * @property string $name + * @property \Carbon\Carbon $from_date + * @property \Carbon\Carbon $to_date + * @property int $minimum_stay + * @property string $description + * @property int $only_weekday + * @property \Illuminate\Database\Eloquent\Collection $fewo_prices + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason whereFromDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason whereMinimumStay($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason whereOnlyWeekday($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\FewoSeason whereToDate($value) + * @property-read int|null $fewo_prices_count + * @mixin \Eloquent + */ + class FewoSeason extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class GeneralFile + * + * @property int $id + * @property int $travel_country_id + * @property string $identifier + * @property string $filename + * @property string $dir + * @property string $original_name + * @property string $ext + * @property string $mine + * @property int $size + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelCountry $travel_country + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereOriginalName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\GeneralFile whereUpdatedAt($value) + * @mixin \Eloquent + */ + class GeneralFile extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQContentCategory + * + * @property int $id + * @property string $name + * @property string $slug + * @property string $identifier + * @property int $pos + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Collection|IQContentTag[] $i_q_content_tags + * @package App\Models + * @property-read int|null $i_q_content_tags_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory findSimilarSlugs($attribute, $config, $slug) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentCategory whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQContentCategory withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class IQContentCategory extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQContentFaq + * + * @property int $id + * @property int $tree_node_id + * @property int $faq_id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property AnswerQuestion $answer_question + * @property IQContentTreeNode $i_q_content_tree_node + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq whereTreeNodeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq whereFaqId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFaq whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQContentFaq extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQContentFileTag + * + * @property int $id + * @property int $file_id + * @property int $tag_id + * @property Carbon $created_at + * @property Carbon $updated_at + * + * //* @property IQContentFile $i_q_content_file + * @property IQContentTag $i_q_content_tag + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag whereFileId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag whereTagId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentFileTag whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQContentFileTag extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\IQContentSite + * + * @property int $id + * @property int $tree_node_id + * @property int $travel_guide_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\IQContentTreeNode $iq_content_tree_node + * @property-read \App\Models\TravelGuide $travel_guide + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite whereTravelGuideId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite whereTreeNodeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite whereUpdatedAt($value) + * @property string|null $identifier + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentSite whereIdentifier($value) + * @mixin \Eloquent + */ + class IQContentSite extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQContentTag + * + * @property int $id + * @property int $category_id + * @property string $name + * @property string $slug + * @property int $pos + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property IQContentCategory $i_q_content_category + * // * @property Collection|IQContentFileTag[] $i_q_content_file_tags + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag whereCategoryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTag whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQContentTag extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\IQContentTree + * + * @property int $id + * @property string $name + * @property string $identifier + * @property string $slug + * @property string|null $description + * @property array|null $settings + * @property int $pos + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentTreeNode[] $iq_content_tree_nodes + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree findSimilarSlugs($attribute, $config, $slug) + * @method static bool|null forceDelete() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree newQuery() + * @method static \Illuminate\Database\Query\Builder|\App\Models\IQContentTree onlyTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree query() + * @method static bool|null restore() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereSettings($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereUpdatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\App\Models\IQContentTree withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\Models\IQContentTree withoutTrashed() + * @property int|null $page_id + * @property int|null $root_id + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree wherePageId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTree whereRootId($value) + * @property-read int|null $iq_content_tree_nodes_count + * @method static \Illuminate\Database\Eloquent\Builder|IQContentTree withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class IQContentTree extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\IQContentTreeNode + * + * @property int $id + * @property int $tree_id + * @property int|null $parent_id + * @property int $lvl + * @property string $name + * @property string $identifier + * @property string $slug + * @property string|null $description + * @property array|null $settings + * @property int $pos + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property-read \App\Models\IQContentTree $iq_content_tree + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentTreeNode[] $iq_content_tree_node_childs + * @property-read \App\Models\IQContentTreeNode|null $iq_content_tree_node_parent + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode findSimilarSlugs($attribute, $config, $slug) + * @method static bool|null forceDelete() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode newQuery() + * @method static \Illuminate\Database\Query\Builder|\App\Models\IQContentTreeNode onlyTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode query() + * @method static bool|null restore() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereLvl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereParentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereSettings($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereTreeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereUpdatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\App\Models\IQContentTreeNode withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\Models\IQContentTreeNode withoutTrashed() + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentSite[] $iq_content_site + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentSite[] $iq_content_sites + * @property-read int|null $iq_content_sites_count + * @property-read int|null $iq_content_tree_node_childs_count + * @property string|null $title + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereTitle($value) + * @property array|null $image + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\IQContentTreeNode whereImage($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentFaq[] $iq_content_faq + * @property-read int|null $iq_content_faq_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentFaq[] $iq_content_faqs + * @property-read int|null $iq_content_faqs_count + * @method static \Illuminate\Database\Eloquent\Builder|IQContentTreeNode withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class IQContentTreeNode extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQTravelGroup + * + * @property int $id + * @property string $name + * @property string $description + * @property string $highlights + * @property int $days_start + * @property int $days_duration + * @property int $min_persons + * @property float $price_adult + * @property float $price_children + * @property int $pos + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Collection|IQTravelGroupItem[] $i_q_travel_group_items + * @package App\Models + * @property-read int|null $i_q_travel_group_items_count + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup query() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereDaysDuration($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereDaysStart($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereHighlights($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereMinPersons($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup wherePriceAdult($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup wherePriceChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroup whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQTravelGroup extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQTravelGroupItem + * + * @property int $id + * @property int $i_q_travel_group_id + * @property int $i_q_travel_item_id + * @property int $pos + * @property Carbon $created_at + * @property Carbon $updated_at + * @property IQTravelGroup $i_q_travel_group + * @property IQTravelItem $i_q_travel_item + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem query() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem whereIQTravelGroupId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem whereIQTravelItemId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelGroupItem whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQTravelGroupItem extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQTravelItem + * + * @property int $id + * @property string $name + * @property string $description + * @property string $highlights + * @property int $draft_type_id + * @property int $travel_country_id + * @property int $days_start + * @property int $days_duration + * @property int $min_persons + * @property float $price_adult + * @property float $price_children + * @property int $pos + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelCountry $travel_country + * @property Collection|IQTravelGroupItem[] $i_q_travel_group_items + * @property Collection|IQTravelItemPlace[] $i_q_travel_item_places + * @package App\Models + * @property-read \App\Models\DraftType|null $draft_type + * @property-read int|null $i_q_travel_group_items_count + * @property-read int|null $i_q_travel_item_places_count + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem query() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereDaysDuration($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereDaysStart($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereDraftTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereHighlights($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereMinPersons($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem wherePriceAdult($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem wherePriceChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItem whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQTravelItem extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQTravelItemPlace + * + * @property int $id + * @property int $i_q_travel_item_id + * @property int $travel_place_id + * @property int $pos + * @property Carbon $created_at + * @property Carbon $updated_at + * @property IQTravelItem $i_q_travel_item + * @property TravelPlace $travel_place + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace query() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace whereIQTravelItemId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace whereTravelPlaceId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelItemPlace whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQTravelItemPlace extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQTravelProgram + * + * @property int $id + * @property string $name + * @property string $code + * @property int $travel_country_id + * @property string $description + * @property string $highlights + * @property string $not_included + * @property string $weekdays + * @property int $days_duration + * @property float $discount + * @property float $deposit_pro + * @property float $price_adult_total + * @property float $price_children_total + * @property string $travel_insurance + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelCountry $travel_country + * @property Collection|IQTravelProgramItem[] $i_q_travel_program_items + * @package App\Models + * @property int|null $typ + * @property-read int|null $i_q_travel_program_items_count + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram query() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereDaysDuration($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereDepositPro($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereDiscount($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereHighlights($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereNotIncluded($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram wherePriceAdultTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram wherePriceChildrenTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereTravelInsurance($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereTyp($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgram whereWeekdays($value) + * @mixin \Eloquent + */ + class IQTravelProgram extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class IQTravelProgramItem + * + * @property int $id + * @property int $i_q_travel_program_id + * @property int $i_q_travel_item_id + * @property int $i_q_travel_group_id + * @property int $typ + * @property int $pos + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property IQTravelGroup $i_q_travel_group + * @property IQTravelItem $i_q_travel_item + * @property IQTravelProgram $i_q_travel_program + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem query() + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereIQTravelGroupId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereIQTravelItemId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereIQTravelProgramId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereTyp($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQTravelProgramItem whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQTravelProgramItem extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class InitialContactType + * + * @property int $id + * @property string $name + * @property Collection|Lead[] $leads + * @package App\Models + * @property-read int|null $leads_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InitialContactType newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InitialContactType newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InitialContactType query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InitialContactType whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InitialContactType whereName($value) + * @mixin \Eloquent + */ + class InitialContactType extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Inquiry + * + * @property int $id + * @property int $lead_id + * @property int $template_id + * @property bool $in_pdf + * @property Carbon $begin + * @property Carbon $end + * @property int $type_id + * @property string $type_s + * @property string $data_s + * @property int $view_position + * @property Lead $lead + * @property InquiryTemplate $inquiry_template + * @property InquiryType $inquiry_type + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereBegin($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereDataS($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereEnd($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereInPdf($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereTemplateId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereTypeS($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Inquiry whereViewPosition($value) + * @mixin \Eloquent + */ + class Inquiry extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class InquiryTemplate + * + * @property int $id + * @property string $title + * @property Collection|Inquiry[] $inquiries + * @package App\Models + * @property-read int|null $inquiries_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryTemplate newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryTemplate newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryTemplate query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryTemplate whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryTemplate whereTitle($value) + * @mixin \Eloquent + */ + class InquiryTemplate extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class InquiryType + * + * @property int $id + * @property string $name + * @property int $arrangement_type_id + * @property ArrangementType $arrangement_type + * @property Collection|Inquiry[] $inquiries + * @package App\Models + * @property-read int|null $inquiries_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryType newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryType newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryType query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryType whereArrangementTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryType whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InquiryType whereName($value) + * @mixin \Eloquent + */ + class InquiryType extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Insurance + * + * @property int $id + * @property string $name + * @property string $name_full + * @property string $contact_emails + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance whereContactEmails($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Insurance whereUpdatedAt($value) + * @mixin \Eloquent + */ + class Insurance extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class InsuranceCertificate + * + * @property int $id + * @property int $booking_id + * @property int $internal_id + * @property string $filename + * @property boolean $binary_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $request_data + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereInternalId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereRequestData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\InsuranceCertificate whereUpdatedAt($value) + * @mixin \Eloquent + */ + class InsuranceCertificate extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Keyword + * + * @property int $id + * @property string $name + * @property Carbon $created_at + * @property Carbon $updated_at + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Keyword newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Keyword newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Keyword query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Keyword whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Keyword whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Keyword whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Keyword whereUpdatedAt($value) + * @mixin \Eloquent + */ + class Keyword extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Lead + * + * @property int $id + * @property int $customer_id + * @property Carbon $request_date + * @property Carbon $travelperiod_start + * @property Carbon $travelperiod_end + * @property int $travelperiod_length + * @property int $travelcountry_id + * @property int $travelagenda_id + * @property string $remarks + * @property int $sf_guard_user_id + * @property bool $is_closed + * @property int $initialcontacttype_id + * @property int $searchengine_id + * @property string $searchengine_keywords + * @property int $status_id + * @property Carbon $next_due_date + * @property int $website_id + * @property int $travelcategory_id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property float $price + * @property int $pax + * @property string $participant_name + * @property string $participant_firstname + * @property Carbon $participant_birthdate + * @property int $participant_salutation_id + * @property Customer $customer + * @property InitialContactType $initial_contact_type + * @property Salutation $salutation + * @property Searchengine $searchengine + * @property SfGuardUser $sf_guard_user + * @property Status $status + * @property TravelAgenda $travel_agenda + * @property TravelCategory $travel_category + * @property TravelCountry $travel_country + * @property Website $website + * @property Collection|Booking[] $bookings + * @property Collection|Inquiry[] $inquiries + * @property Collection|LeadParticipant[] $lead_participants + * @property Collection|Offer[] $offers + * @property Collection|StatusHistory[] $status_histories + * @package App\Models + * @property-read int|null $bookings_count + * @property-read int|null $inquiries_count + * @property-read int|null $lead_participants_count + * @property-read int|null $offers_count + * @property-read int|null $status_histories_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereInitialcontacttypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereIsClosed($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereNextDueDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereParticipantBirthdate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereParticipantFirstname($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereParticipantName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereParticipantSalutationId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead wherePax($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead wherePrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereRemarks($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereRequestDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereSearchengineId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereSearchengineKeywords($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereSfGuardUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereStatusId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereTravelagendaId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereTravelcategoryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereTravelcountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereTravelperiodEnd($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereTravelperiodLength($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereTravelperiodStart($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereWebsiteId($value) + * @property-read \App\Models\Sym\TravelCountry|null $travel_country_crm + * @property bool|null $is_rebook + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Lead whereIsRebook($value) + * @property-read Collection|\App\Models\LeadFile[] $lead_files + * @property-read int|null $lead_files_count + * @property-read \App\Models\LeadMail|null $lead_mail_last + * @property-read Collection|\App\Models\LeadMail[] $lead_mails + * @property-read int|null $lead_mails_count + * @property-read Collection|\App\Models\LeadMail[] $lead_mails_sent_at + * @property-read int|null $lead_mails_sent_at_count + * @property-read Collection|\App\Models\LeadNotice[] $lead_notices + * @property-read int|null $lead_notices_count + * @mixin \Eloquent + */ + class Lead extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class LeadFile + * + * @property int $id + * @property int $lead_id + * @property int $lead_mail_id + * @property string $identifier + * @property string $filename + * @property string $dir + * @property string $original_name + * @property string $ext + * @property string $mine + * @property int $size + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Lead $lead + * @property LeadMail $lead_mail + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile query() + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereLeadMailId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereOriginalName($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadFile whereUpdatedAt($value) + * @mixin \Eloquent + */ + class LeadFile extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class LeadMail + * + * @property int $id + * @property int $lead_id + * @property int $customer_id + * @property bool $is_answer + * @property int $reply_id + * @property string $email + * @property string $recipient + * @property string $cc + * @property string $bcc + * @property string $subject + * @property string $message + * @property int $dir + * @property int $subdir + * @property bool $draft + * @property bool $important + * @property bool $send + * @property bool $fail + * @property string $error + * @property string $forward + * @property Carbon $sent_at + * @property Carbon $scheduled_at + * @property Carbon $delivered_at + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Customer $customer + * @property Lead $lead + * @property CustomerMail $customer_mail + * @property Collection|LeadFile[] $lead_files + * @package App\Models + * @property-read int|null $lead_files_count + * @property-read LeadMail|null $lead_mail + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail query() + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereBcc($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereCc($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereDeliveredAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereDraft($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereError($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereFail($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereForward($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereImportant($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereIsAnswer($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereRecipient($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereReplyId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereScheduledAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereSend($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereSentAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereSubdir($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereSubject($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadMail whereUpdatedAt($value) + * @mixin \Eloquent + */ + class LeadMail extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class LeadNotice + * + * @property int $id + * @property int $lead_id + * @property int $from_user_id + * @property int $to_user_id + * @property string $message + * @property bool $show + * @property bool $important + * @property Carbon $edit_at + * @property Carbon $created_at + * @property Carbon $updated_at + * @property User $user + * @property Lead $lead + * @package App\Models + * @property-read User $from_user + * @property-read User|null $to_user + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice query() + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereEditAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereFromUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereImportant($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereShow($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereToUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadNotice whereUpdatedAt($value) + * @mixin \Eloquent + */ + class LeadNotice extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class LeadParticipant + * + * @property int $id + * @property int $lead_id + * @property string $participant_name + * @property string $participant_firstname + * @property Carbon $participant_birthdate + * @property int $participant_salutation_id + * @property Lead $lead + * @property Salutation $salutation + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant whereParticipantBirthdate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant whereParticipantFirstname($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant whereParticipantName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\LeadParticipant whereParticipantSalutationId($value) + * @property int|null $participant_child + * @property int|null $nationality_id + * @property-read \App\Models\TravelNationality|null $travel_nationality + * @method static \Illuminate\Database\Eloquent\Builder|LeadParticipant whereNationalityId($value) + * @method static \Illuminate\Database\Eloquent\Builder|LeadParticipant whereParticipantChild($value) + * @mixin \Eloquent + */ + class LeadParticipant extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\News + * + * @property int $id + * @property int|null $owner + * @property string|null $model + * @property int|null $lvl + * @property int $owner_second + * @property int|null $catalog_id + * @property int|null $catalog_index + * @property string|null $slug + * @property int|null $travel_program + * @property int|null $status + * @property int|null $show_in_navi + * @property int|null $order + * @property string|null $title + * @property string|null $pagetitle + * @property string|null $description + * @property string|null $keywords + * @property string|null $content + * @property string|null $content_new + * @property string|null $buma_destination + * @property int|null $OLD_CatalogID + * @property int|null $OLD_OwnerID + * @property int|null $buma_gjr + * @property string|null $date + * @property int $price-tags + * @property string|null $text_right + * @property string|null $keyword + * @property string|null $canonical_url + * @property int|null $country_id + * @property string|null $template + * @property int|null $lft + * @property int|null $rgt + * @property int|null $tree_root + * @property int|null $parent_id + * @property string|null $real_url_path + * @property string|null $box_body + * @property string|null $box_image_url + * @property string|null $box_star + * @property string|null $box_discount + * @property string|null $cms_settings + * @property int|null $fewo_lodging + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\News[] $children + * @property-read \App\Models\News|null $parent + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereBoxBody($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereBoxDiscount($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereBoxImageUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereBoxStar($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereBumaDestination($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereBumaGjr($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereCanonicalUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereCatalogId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereCatalogIndex($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereCmsSettings($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereContent($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereContentNew($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereFewoLodging($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereKeyword($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereKeywords($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereLft($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereLvl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereModel($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereOLDCatalogID($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereOLDOwnerID($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereOrder($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereOwner($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereOwnerSecond($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News wherePagetitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereParentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News wherePriceTags($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereRealUrlPath($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereRgt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereShowInNavi($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereTemplate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereTextRight($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereTravelProgram($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereTreeRoot($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereUpdatedAt($value) + * @property int|null $travel_guide_content_id + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereTravelGuideContentId($value) + * @property string|null $title_short + * @property string|null $before_title + * @property-read int|null $children_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereBeforeTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\News whereTitleShort($value) + * @property-read \Illuminate\Database\Eloquent\Collection $child_pages + * @property-read int|null $child_pages_count + * @property-read \App\Models\Page|null $page + * @property-read \Illuminate\Database\Eloquent\Collection $pages + * @property-read int|null $pages_count + * @property-read \App\Models\Page|null $parent_page + * @property-read \Illuminate\Database\Eloquent\Collection $travel_countries + * @property-read int|null $travel_countries_count + * @property-read \App\Models\TravelCountry|null $travel_country + * @property-read \App\Models\TravelGuide|null $travel_guide + * @property-read \App\Models\TravelProgram|null $travel_program_content + * @method static \Illuminate\Database\Eloquent\Builder|Page findSimilarSlugs(string $attribute, array $config, string $slug) + * @method static \Illuminate\Database\Eloquent\Builder|Page withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class News extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class NewsletterContact + * + * @property int $id + * @property string $email + * @property string|null $firstname + * @property string|null $lastname + * @property bool $group_kulturreisen + * @property bool $group_ferienwohnungen + * @property string $source + * @property string $status + * @property Carbon|null $subscribed_at + * @property Carbon|null $unsubscribed_at + * @property Carbon|null $last_booking_at + * @property Carbon|null $last_travel_end_date + * @property int $total_bookings_kulturreisen + * @property int $total_bookings_ferienwohnungen + * @property int|null $customer_id + * @property int|null $travel_user_id + * @property Carbon|null $last_synced_at + * @property string|null $sync_hash + * @property string|null $notes + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Carbon|null $deleted_at + * @property-read Customer|null $customer + * @property-read TravelUser|null $travel_user + * @property-read Collection|NewsletterLog[] $logs + * @package App\Models + * @property-read mixed $full_name + * @property-read mixed $groups + * @property-read mixed $groups_string + * @property-read mixed $source_label + * @property-read mixed $status_badge + * @property-read mixed $status_color + * @property-read mixed $status_label + * @property-read mixed $total_bookings + * @property-read int|null $logs_count + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact active() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact ferienwohnungen() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact kulturreisen() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact multipleBookers() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact onlyTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact query() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereFirstname($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereGroupFerienwohnungen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereGroupKulturreisen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastBookingAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastSyncedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastTravelEndDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastname($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereNotes($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereSource($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereSubscribedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereSyncHash($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereTotalBookingsFerienwohnungen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereTotalBookingsKulturreisen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereTravelUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereUnsubscribedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact withBookings() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact withTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact withoutTrashed() + * @mixin \Eloquent + */ + class NewsletterContact extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class NewsletterLog + * + * @property int $id + * @property int $newsletter_contact_id + * @property string $action + * @property string|null $description + * @property array|null $metadata + * @property int|null $user_id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property-read NewsletterContact $newsletter_contact + * @property-read SfGuardUser|null $user + * @package App\Models + * @property-read mixed $action_label + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog query() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereAction($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereMetadata($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereNewsletterContactId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereUserId($value) + * @mixin \Eloquent + */ + class NewsletterLog extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Offer + * + * @property int $id + * @property int $lead_id + * @property float $total + * @property boolean $binary_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Lead $lead + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer whereBinaryData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer whereTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Offer whereUpdatedAt($value) + * @mixin \Eloquent + */ + class Offer extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Page + * + * @property int $id + * @property int|null $owner + * @property string|null $model + * @property int|null $lvl + * @property int $owner_second + * @property int|null $catalog_id + * @property int|null $catalog_index + * @property string|null $slug + * @property int|null $travel_program + * @property int|null $status + * @property int|null $show_in_navi + * @property int|null $order + * @property string|null $title + * @property string|null $title_short + * @property string|null $before_title + * @property string|null $pagetitle + * @property string|null $description + * @property string|null $keywords + * @property string|null $content + * @property string|null $content_new + * @property string|null $buma_destination + * @property int|null $OLD_CatalogID + * @property int|null $OLD_OwnerID + * @property int|null $buma_gjr + * @property Carbon $date + * @property bool $price-tags + * @property string|null $text_right + * @property string|null $keyword + * @property string|null $canonical_url + * @property int|null $country_id + * @property string|null $template + * @property int|null $lft + * @property int|null $rgt + * @property int|null $tree_root + * @property int|null $parent_id + * @property string|null $real_url_path + * @property int|null $travel_guide_content_id + * @property string|null $box_body + * @property string|null $box_image_url + * @property string|null $box_star + * @property string|null $box_discount + * @property string|null $cms_settings + * @property int|null $fewo_lodging + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property Page|null $page + * @property TravelCountry|null $travel_country + * @property TravelGuide|null $travel_guide + * @property Collection|Page[] $pages + * @property Collection|Redirect[] $redirects + * @property Collection|TravelCountry[] $travel_countries + * @property Collection|TravelProgram[] $travel_program_content + * @package App\Models + * @property-read Collection $child_pages + * @property-read int|null $child_pages_count + * @property-read int|null $pages_count + * @property-read Page|null $parent_page + * @property-read int|null $travel_countries_count + * @method static \Illuminate\Database\Eloquent\Builder|Page findSimilarSlugs(string $attribute, array $config, string $slug) + * @method static \Illuminate\Database\Eloquent\Builder|Page newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Page newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Page query() + * @method static \Illuminate\Database\Eloquent\Builder|Page whereBeforeTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereBoxBody($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereBoxDiscount($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereBoxImageUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereBoxStar($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereBumaDestination($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereBumaGjr($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereCanonicalUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereCatalogId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereCatalogIndex($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereCmsSettings($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereContent($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereContentNew($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereFewoLodging($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereKeyword($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereKeywords($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereLft($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereLvl($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereModel($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereOLDCatalogID($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereOLDOwnerID($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereOrder($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereOwner($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereOwnerSecond($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page wherePagetitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereParentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page wherePriceTags($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereRealUrlPath($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereRgt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereShowInNavi($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereTemplate($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereTextRight($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereTitleShort($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereTravelGuideContentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereTravelProgram($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereTreeRoot($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Page withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class Page extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Participant + * + * @property int $id + * @property int $booking_id + * @property string $participant_name + * @property string $participant_firstname + * @property Carbon $participant_birthdate + * @property int $participant_salutation_id + * @property bool $participant_child + * @property Booking $booking + * @property Salutation $salutation + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereParticipantBirthdate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereParticipantChild($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereParticipantFirstname($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereParticipantName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereParticipantSalutationId($value) + * @property int|null $nationality_id + * @property-read \App\Models\TravelNationality|null $travel_nationality + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Participant whereNationalityId($value) + * @property bool|null $participant_pass + * @property bool|null $participant_storno + * @method static \Illuminate\Database\Eloquent\Builder|Participant whereParticipantPass($value) + * @method static \Illuminate\Database\Eloquent\Builder|Participant whereParticipantStorno($value) + * @mixin \Eloquent + */ + class Participant extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Salutation + * + * @property int $id + * @property string $name + * @property Collection|Customer[] $customers + * @property Collection|Lead[] $leads + * @property Collection|LeadParticipant[] $lead_participants + * @property Collection|Participant[] $participants + * @package App\Models + * @property-read int|null $customers_count + * @property-read int|null $lead_participants_count + * @property-read int|null $leads_count + * @property-read int|null $participants_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Salutation newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Salutation newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Salutation query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Salutation whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Salutation whereName($value) + * @mixin \Eloquent + */ + class Salutation extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Searchengine + * + * @property int $id + * @property string $name + * @property Collection|Lead[] $leads + * @package App\Models + * @property-read int|null $leads_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Searchengine newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Searchengine newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Searchengine query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Searchengine whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Searchengine whereName($value) + * @mixin \Eloquent + */ + class Searchengine extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class ServiceProvider + * + * @property int $id + * @property string $name + * @property bool $dollar + * @property string $type + * @property Collection|ServiceProviderEntry[] $service_provider_entries + * @package App\Models + * @property-read int|null $service_provider_entries_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider whereDollar($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider whereType($value) + * @property array|null $contact_emails + * @property bool|null $active + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProvider whereContactEmails($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ServiceProviderService[] $service_provider_services + * @property-read int|null $service_provider_services_count + * @mixin \Eloquent + */ + class ServiceProvider extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class ServiceProviderEntry + * + * @property int $id + * @property int $booking_id + * @property int $service_provider_id + * @property float $amount + * @property float $amount_eur + * @property float $factor + * @property Carbon $payment_date + * @property string $invoice_number + * @property bool $is_cleared + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $type + * @property Booking $booking + * @property ServiceProvider $service_provider + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereAmount($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereAmountEur($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereFactor($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereInvoiceNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereIsCleared($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry wherePaymentDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereServiceProviderId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereType($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderEntry whereUpdatedAt($value) + * @mixin \Eloquent + */ + class ServiceProviderEntry extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class ServiceProviderService + * + * @property int $id + * @property int $service_provider_id + * @property string $name + * @property string $description + * @property bool $active + * @property int $pos + * @property Carbon $created_at + * @property Carbon $updated_at + * @property ServiceProvider $service_provider + * @property Collection|BookingProviderService[] $booking_provider_services + * @package App\Models + * @property-read int|null $booking_provider_services_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService whereServiceProviderId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ServiceProviderService whereUpdatedAt($value) + * @mixin \Eloquent + */ + class ServiceProviderService extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\SfGuardUser + * + * @property int $id + * @property int|null $user_id + * @property string $username + * @property string $algorithm + * @property string|null $salt + * @property string|null $password + * @property int|null $is_active + * @property int|null $is_super_admin + * @property string|null $last_login + * @property string|null $first_name + * @property string|null $last_name + * @property string|null $email_address + * @property string|null $default_page + * @property int|null $branch_id + * @property string|null $api_key + * @property string|null $identify + * @property string|null $token + * @property string|null $token_at + * @property \Illuminate\Support\Carbon $created_at + * @property \Illuminate\Support\Carbon $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereAlgorithm($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereApiKey($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereBranchId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereDefaultPage($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereEmailAddress($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereFirstName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereIdentify($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereIsActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereIsSuperAdmin($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereLastLogin($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereLastName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser wherePassword($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereSalt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereToken($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereTokenAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser whereUsername($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SfGuardUser query() + * @property-read \App\User|null $user + * @property-read mixed $fullname + * @mixin \Eloquent + */ + class SfGuardUser extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\SidebarWidget + * + * @property int $id + * @property string $name + * @property string|null $component + * @property string|null $html + * @property array|null $show_at + * @property int|null $pos + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereComponent($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereHtml($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereShowAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\SidebarWidget query() + * @mixin \Eloquent + */ + class SidebarWidget extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Status + * + * @property int $id + * @property string $name + * @property int $handling_days + * @property string $color + * @property Collection|Lead[] $leads + * @property Collection|StatusHistory[] $status_histories + * @package App\Models + * @property-read int|null $leads_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Status newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Status newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Status query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Status whereColor($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Status whereHandlingDays($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Status whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Status whereName($value) + * @mixin \Eloquent + */ + class Status extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class StatusHistory + * + * @property int $id + * @property int $status_id + * @property int $lead_id + * @property int $sf_guard_user_id + * @property Carbon $date + * @property string $remarks + * @property Carbon $target_date + * @property Carbon $created_at + * @property Lead $lead + * @property SfGuardUser $sf_guard_user + * @property Status $status + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereLeadId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereRemarks($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereSfGuardUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereStatusId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\StatusHistory whereTargetDate($value) + * @mixin \Eloquent + */ + class StatusHistory extends \Eloquent {} +} + +namespace App\Models\Sym{ +/** + * App\Models\Sym\Arrangement + * + * @property int $id + * @property int|null $template_id + * @property string|null $state + * @property string|null $begin + * @property string|null $end + * @property string|null $type_s + * @property string|null $data_s + * @property int|null $view_position + * @property int|null $booking_id + * @property int $type_id + * @property int|null $in_pdf + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereBegin($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereDataS($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereEnd($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereInPdf($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereState($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereTemplateId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereTypeS($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement whereViewPosition($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\Arrangement query() + * @mixin \Eloquent + */ + class Arrangement extends \Eloquent {} +} + +namespace App\Models\Sym{ +/** + * App\Models\Sym\ArrangementTemplate + * + * @property int $id + * @property string|null $title + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Sym\Arrangement[] $arrangements + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\ArrangementTemplate whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\ArrangementTemplate whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\ArrangementTemplate newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\ArrangementTemplate newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\ArrangementTemplate query() + * @property-read int|null $arrangements_count + * @mixin \Eloquent + */ + class ArrangementTemplate extends \Eloquent {} +} + +namespace App\Models\Sym{ +/** + * App\Models\Sym\CmsContent + * + * @property int $id + * @property string $name + * @property string $slug + * @property string $field + * @property string|null $text + * @property string|null $full_text + * @property int|null $integer + * @property float|null $decimal + * @property string|null $created_at + * @property string|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereDecimal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereField($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereFullText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereInteger($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent findSimilarSlugs($attribute, $config, $slug) + * @property string|null $identifier + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereIdentifier($value) + * @property array|null $object + * @property int|null $pos + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent whereObject($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\CmsContent wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|CmsContent withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class CmsContent extends \Eloquent {} +} + +namespace App\Models\Sym{ +/** + * App\Models\Sym\TravelCountry + * + * @property int $id + * @property string $name + * @property int|null $is_customer_country + * @property int|null $active_backend + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereActiveBackend($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereIsCustomerCountry($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry query() + * @property string|null $contact_headline + * @property string|null $contact_text_1 + * @property string|null $contact_text_2 + * @property string|null $contact_text_3 + * @property string|null $contact_text_4 + * @property string|null $contact_footer + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactFooter($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactHeadline($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactText1($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactText2($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactText3($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactText4($value) + * @property array|null $contact_lands + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactLands($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Booking[] $bookings + * @property-read int|null $bookings_count + * @property array|null $contact_emails + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereContactEmails($value) + * @property array|null $mail_dirs + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereMailDirs($value) + * @property string|null $mail_dir_name + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereMailDirName($value) + * @property-read \App\Models\TravelCountry|null $stern_travel_country + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelCountryService[] $travel_country_services + * @property-read int|null $travel_country_services_count + * @property string|null $destco + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Sym\TravelCountry whereDestco($value) + * @property string|null $visum_text + * @method static \Illuminate\Database\Eloquent\Builder|TravelCountry whereVisumText($value) + * @mixin \Eloquent + */ + class TravelCountry extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelAgenda + * + * @property int $id + * @property string $name + * @property int|null $travelcountry_id + * @property int|null $active + * @property-read \App\Models\TravelCountry|null $travel_country + * @property-read \App\Models\Sym\TravelCountry|null $travel_country_crm + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelAgenda whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelAgenda whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelAgenda whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelAgenda whereTravelcountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelAgenda newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelAgenda newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelAgenda query() + * @mixin \Eloquent + */ + class TravelAgenda extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelInsurance + * + * @property int $id + * @property int $booking_id + * @property string $policy_number + * @property string $ak + * @property string $an + * @property string $akt + * @property float $premium + * @property string $request_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAk($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAkt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAn($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance wherePolicyNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance wherePremium($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereRequestData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereUpdatedAt($value) + * @property string|null $name + * @property int|null $travel_country_id + * @property int|null $active + * @property-read \App\Models\TravelCountry|null $travel_country + * @property-read \Illuminate\Database\Eloquent\Collection $travel_programs + * @property-read int|null $travel_programs_count + * @method static \Illuminate\Database\Eloquent\Builder|TravelArrivalPoint whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelArrivalPoint whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelArrivalPoint whereTravelCountryId($value) + * @mixin \Eloquent + */ + class TravelArrivalPoint extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelBooking + * + * @property int $id + * @property int|null $crm_booking_id + * @property int|null $salutation_id + * @property string|null $first_name + * @property string|null $last_name + * @property string|null $street + * @property string|null $zipcode + * @property string|null $city + * @property int|null $country_id + * @property string|null $fax + * @property string|null $phone + * @property string|null $mobile + * @property string|null $email + * @property string|null $comments + * @property \Illuminate\Support\Carbon|null $created + * @property \Illuminate\Support\Carbon|null $selected_start_date + * @property \Illuminate\Support\Carbon|null $selected_end_date + * @property string|null $program_name + * @property array|null $selected_travel + * @property array|null $selected_departure + * @property int|null $program_id + * @property int|null $period_id + * @property string|null $class + * @property int|null $selected_adults + * @property int|null $selected_childs + * @property int|null $participants_total + * @property array|null $participants + * @property array|null $drafts + * @property array|null $service_items + * @property array|null $arrangements + * @property array|null $rooms + * @property float|null $price + * @property float|null $price_total + * @property float|null $deposit_total + * @property float|null $final_payment + * @property \Illuminate\Support\Carbon|null $final_payment_date + * @property string|null $insurance_name + * @property array|null $insurances + * @property int|null $insurance_offer + * @property int|null $travel_cancellation + * @property array|null $options + * @property array $class_options + * @property array $extra_category + * @property bool|null $accept_legal_rights + * @property string|null $ip + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking query() + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereAcceptLegalRights($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereArrangements($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereCity($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereClass($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereClassOptions($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereComments($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereCreated($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereCrmBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereDepositTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereDrafts($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereExtraCategory($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereFax($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereFinalPayment($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereFinalPaymentDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereFirstName($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereInsuranceName($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereInsuranceOffer($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereInsurances($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereIp($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereLastName($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereMobile($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereOptions($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereParticipants($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereParticipantsTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking wherePeriodId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking wherePhone($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking wherePrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking wherePriceTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereProgramId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereProgramName($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereRooms($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereSalutationId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereSelectedAdults($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereSelectedChilds($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereSelectedDeparture($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereSelectedEndDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereSelectedStartDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereSelectedTravel($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereServiceItems($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereStreet($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereTravelCancellation($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelBooking whereZipcode($value) + * @mixin \Eloquent + */ + class TravelBooking extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelBookingFewoChannel + * + * @property int $id + * @property string $name + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property \Illuminate\Database\Eloquent\Collection $travel_user_booking_fewos + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelBookingFewoChannel newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelBookingFewoChannel newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelBookingFewoChannel query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelBookingFewoChannel whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelBookingFewoChannel whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelBookingFewoChannel whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelBookingFewoChannel whereUpdatedAt($value) + * @property-read int|null $travel_user_booking_fewos_count + * @mixin \Eloquent + */ + class TravelBookingFewoChannel extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelCategory + * + * @property int $id + * @property string $name + * @property Collection|Booking[] $bookings + * @property Collection|Lead[] $leads + * @package App\Models + * @property-read int|null $bookings_count + * @property-read int|null $leads_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCategory newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCategory newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCategory query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCategory whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCategory whereName($value) + * @property int|null $active + * @property-read Collection $travel_programs + * @property-read int|null $travel_programs_count + * @method static \Illuminate\Database\Eloquent\Builder|TravelCategory whereActive($value) + * @mixin \Eloquent + */ + class TravelCategory extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelClass + * + * @property int $id + * @property string|null $name + * @property string|null $description + * @property int|null $program_id + * @property int $standard + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\TravelProgram|null $travel_program + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass whereProgramId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass whereStandard($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass whereUpdatedAt($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelProgramDraft[] $travel_program_drafts + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelClass query() + * @property-read int|null $travel_program_drafts_count + * @mixin \Eloquent + */ + class TravelClass extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelCompany + * + * @property int $id + * @property string $name + * @property float $percentage + * @property bool $is_allowed_edit_commission + * @property bool $is_inhouse + * @property Collection|Booking[] $bookings + * @property Collection|BookingServiceItem[] $booking_service_items + * @package App\Models + * @property-read int|null $booking_service_items_count + * @property-read int|null $bookings_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany whereIsAllowedEditCommission($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany whereIsInhouse($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany wherePercentage($value) + * @property int|null $active + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany whereActive($value) + * @property array|null $contact_emails + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompany whereContactEmails($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelCompanyService[] $travel_company_services + * @property-read int|null $travel_company_services_count + * @mixin \Eloquent + */ + class TravelCompany extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelCompanyService + * + * @property int $id + * @property int $travel_company_id + * @property string $name + * @property string $description + * @property bool $active + * @property int $pos + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelCompany $travel_company + * @property Collection|BookingCompanyService[] $booking_company_services + * @package App\Models + * @property-read int|null $booking_company_services_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService whereTravelCompanyId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCompanyService whereUpdatedAt($value) + * @mixin \Eloquent + */ + class TravelCompanyService extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelCountry + * + * @property int $id + * @property int|null $crm_id + * @property string $name + * @property string|null $html_information + * @property string|null $entry_requirements + * @property int|null $feedback_page_id + * @property int|null $is_customer_country + * @property int|null $active_frontend + * @property int|null $active_backend + * @property string|null $updated_at + * @property string|null $created_at + * @property-read \App\Models\Page|null $feedback_page + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelNationalityRequirement[] $travel_nationality_requirements + * @property-read \App\Models\TravelProgramCountry $travel_program_country + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereActiveBackend($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereActiveFrontend($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereCrmId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereEntryRequirements($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereFeedbackPageId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereHtmlInformation($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereIsCustomerCountry($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry query() + * @property string|null $slug + * @property string|null $text_before + * @property string|null $text_after + * @property string|null $contact_headline + * @property string|null $contact_text_1 + * @property string|null $contact_text_2 + * @property string|null $contact_text_3 + * @property string|null $contact_text_4 + * @property string|null $contact_footer + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactFooter($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactHeadline($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactText1($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactText2($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactText3($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactText4($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereTextAfter($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereTextBefore($value) + * @property array|null $contact_lands + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactLands($value) + * @property-read int|null $travel_nationality_requirements_count + * @property array|null $contact_emails + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereContactEmails($value) + * @property array|null $mail_dirs + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereMailDirs($value) + * @property string $mail_dir_name + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereMailDirName($value) + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GeneralFile[] $general_files + * @property-read int|null $general_files_count + * @property-read \App\Models\Sym\TravelCountry|null $crm_travel_country + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelCountryService[] $travel_country_services + * @property-read int|null $travel_country_services_count + * @property string|null $destco + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountry whereDestco($value) + * @property string|null $visum_text + * @method static \Illuminate\Database\Eloquent\Builder|TravelCountry whereVisumText($value) + * @mixin \Eloquent + */ + class TravelCountry extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelCountryService + * + * @property int $id + * @property int $travel_country_id + * @property int $crm_travel_country + * @property string $name + * @property string $description + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelCountry $travel_country + * @property Collection|BookingCountryService[] $booking_country_services + * @package App\Models + * @property int|null $crm_travel_country_id + * @property int|null $pos + * @property-read int|null $booking_country_services_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereCrmTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelCountryService whereUpdatedAt($value) + * @mixin \Eloquent + */ + class TravelCountryService extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelInsurance + * + * @property int $id + * @property int $booking_id + * @property string $policy_number + * @property string $ak + * @property string $an + * @property string $akt + * @property float $premium + * @property string $request_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAk($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAkt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAn($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance wherePolicyNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance wherePremium($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereRequestData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereUpdatedAt($value) + * @property string|null $name + * @property string $text + * @property-read \Illuminate\Database\Eloquent\Collection $travel_programs + * @property-read int|null $travel_programs_count + * @method static \Illuminate\Database\Eloquent\Builder|TravelGerneralNote whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelGerneralNote whereText($value) + * @mixin \Eloquent + */ + class TravelGerneralNote extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelMagazine + * + * @property int $id + * @property string $name + * @property string $slug + * @property string|null $text + * @property string|null $full_text + * @property string|null $meta_title + * @property string|null $meta_description + * @property string|null $meta_keywords + * @property int|null $pos + * @property int $scope + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide findSimilarSlugs($attribute, $config, $slug) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereFullText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereMetaDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereMetaKeywords($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereMetaTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereScope($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide query() + * @property string|null $keyword + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentSite[] $iq_content_site + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereKeyword($value) + * @property int|null $country_id + * @property string|null $box_image_url + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\IQContentSite[] $iq_content_sites + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereBoxImageUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereCountryId($value) + * @property-read int|null $iq_content_sites_count + * @property int|null $author_id + * @property-read \App\Models\CMSAuthor|null $author + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelGuide whereAuthorId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelGuide withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class TravelGuide extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelInsurance + * + * @property int $id + * @property int $booking_id + * @property string $policy_number + * @property string $ak + * @property string $an + * @property string $akt + * @property float $premium + * @property string $request_data + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Booking $booking + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAk($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAkt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereAn($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereBookingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance wherePolicyNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance wherePremium($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereRequestData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelInsurance whereUpdatedAt($value) + * @mixin \Eloquent + */ + class TravelInsurance extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelNationality + * + * @property int $id + * @property string|null $name + * @property int|null $active + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationality whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationality whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationality whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationality newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationality newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationality query() + * @property string|null $nat + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationality whereNat($value) + * @mixin \Eloquent + */ + class TravelNationality extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelNationalityRequirement + * + * @property int $id + * @property int|null $travel_country_id + * @property int|null $travel_nationality_id + * @property string|null $text + * @property-read \App\Models\TravelCountry|null $travel_country + * @property-read \App\Models\TravelNationality|null $travel_nationality + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationalityRequirement whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationalityRequirement whereText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationalityRequirement whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationalityRequirement whereTravelNationalityId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationalityRequirement newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationalityRequirement newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelNationalityRequirement query() + * @mixin \Eloquent + */ + class TravelNationalityRequirement extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelPageGuide + * + * @property int $id + * @property int|null $owner + * @property string|null $model + * @property int|null $lvl + * @property int $owner_second + * @property int|null $catalog_id + * @property int|null $catalog_index + * @property string|null $slug + * @property int|null $travel_program + * @property int|null $status + * @property int|null $show_in_navi + * @property int|null $order + * @property string|null $title + * @property string|null $pagetitle + * @property string|null $description + * @property string|null $keywords + * @property string|null $content + * @property string|null $content_new + * @property string|null $buma_destination + * @property int|null $OLD_CatalogID + * @property int|null $OLD_OwnerID + * @property int|null $buma_gjr + * @property string $date + * @property int $price-tags + * @property string|null $text_right + * @property string|null $keyword + * @property string|null $canonical_url + * @property int|null $country_id + * @property string|null $template + * @property int|null $lft + * @property int|null $rgt + * @property int|null $tree_root + * @property int|null $parent_id + * @property string|null $real_url_path + * @property int|null $travel_guide_content_id + * @property string|null $box_body + * @property string|null $box_image_url + * @property string|null $box_star + * @property string|null $box_discount + * @property string|null $cms_settings + * @property int|null $fewo_lodging + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereBoxBody($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereBoxDiscount($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereBoxImageUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereBoxStar($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereBumaDestination($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereBumaGjr($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereCanonicalUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereCatalogId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereCatalogIndex($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereCmsSettings($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereContent($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereContentNew($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereFewoLodging($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereKeyword($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereKeywords($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereLft($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereLvl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereModel($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereOLDCatalogID($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereOLDOwnerID($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereOrder($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereOwner($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereOwnerSecond($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide wherePagetitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereParentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide wherePriceTags($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereRealUrlPath($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereRgt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereShowInNavi($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereTemplate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereTextRight($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereTravelGuideContentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereTravelProgram($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereTreeRoot($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereUpdatedAt($value) + * @property string|null $title_short + * @property string|null $before_title + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereBeforeTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelPageGuide whereTitleShort($value) + * @mixin \Eloquent + */ + class TravelPageGuide extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelPlace + * + * @property int $id + * @property string $name + * @property string $description + * @property int $travel_country_id + * @property float $latitude + * @property float $longitude + * @property bool $active + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelCountry $travel_country + * @package App\Models + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace query() + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereLatitude($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereLongitude($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereTravelCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelPlace whereUpdatedAt($value) + * @mixin \Eloquent + */ + class TravelPlace extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelProgram + * + * @property int $id + * @property float|null $profit_margin + * @property int|null $category_id + * @property string|null $program_code + * @property int|null $program_duration + * @property bool|null $is_seasonal + * @property bool|null $youth + * @property string|null $title + * @property string|null $subtitle + * @property string|null $slider_info + * @property int|null $program_type + * @property int|null $organizer + * @property int|null $generalnote + * @property int|null $status + * @property string|null $included + * @property string|null $class_description + * @property string|null $excluded + * @property string|null $advices + * @property string|null $notes + * @property string|null $url + * @property int|null $max_age_for_children + * @property string|null $html_description + * @property int|null $insurance_1 + * @property int|null $insurance_2 + * @property int|null $insurance_3 + * @property int|null $insurance_4 + * @property bool $in_slider + * @property bool $show_map + * @property string|null $map_html + * @property string $map_image + * @property string $map_image_ext + * @property int|null $travel_country + * @property int|null $travel_category + * @property int|null $travel_agenda + * @property int|null $deposit_percent + * @property bool|null $netto_prices_in_euro + * @property string|null $text_right + * @property float|null $default_flight_price + * @property int|null $travel_arrival_point_id + * @property string|null $weekdays + * @property int|null $position + * @property float|null $discount + * @property int|null $discount_is_percent_value + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property TravelArrivalPoint|null $travel_arrival_point + * @property TravelOrganizer|null $travel_organizer + * @property TravelInsurance|null $travel_insurance + * @property TravelGeneralNote|null $travel_general_note + * @property Option $option + * @property Collection|TravelClass[] $travel_classes + * @property Collection|TravelDeparturePoint[] $travel_departure_points + * @property Collection|TravelPeriod[] $travel_periods + * @property TravelProgramCountry $travel_program_country + * @property TravelProgramDestination $travel_program_destination + * @property Collection|TravelProgramDraft[] $travel_program_drafts + * @property Collection|TravelProgramImage[] $travel_program_images + * @property TravelProgramOption $travel_program_option + * @property TravelProgramRelated $travel_program_related + * @package App\Models + * @property int|null $travel_company + * @property-read Collection|\App\Models\TravelClass[] $classes + * @property-read int|null $classes_count + * @property-read int|null $travel_classes_count + * @property-read int|null $travel_program_drafts_count + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram query() + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereAdvices($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereCategoryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereClassDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereDefaultFlightPrice($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereDepositPercent($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereDiscount($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereDiscountIsPercentValue($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereExcluded($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereGeneralnote($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereHtmlDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereInSlider($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereIncluded($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereInsurance1($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereInsurance2($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereInsurance3($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereInsurance4($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereIsSeasonal($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereMapHtml($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereMapImage($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereMapImageExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereMaxAgeForChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereNettoPricesInEuro($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereNotes($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereOrganizer($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram wherePosition($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereProfitMargin($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereProgramCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereProgramDuration($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereProgramType($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereShowMap($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereSliderInfo($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereSubtitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereTextRight($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereTravelAgenda($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereTravelArrivalPointId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereTravelCategory($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereTravelCompany($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereTravelCountry($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereWeekdays($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram whereYouth($value) + * @property string|null $payment_conditions + * @property-read \App\Models\Page|null $page + * @property-read \App\Models\TravelProgramCategory|null $travel_program_category + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgram wherePaymentConditions($value) + * @mixin \Eloquent + */ + class TravelProgram extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelProgramCategory + * + * @property int $id + * @property string|null $name + * @property string $conversion_code + * @property-read int|null $travel_programs_count + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgramCategory newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgramCategory newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgramCategory query() + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgramCategory whereConversionCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgramCategory whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelProgramCategory whereName($value) + * @property-read Collection $travel_programs + * @mixin \Eloquent + */ + class TravelProgramCategory extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelProgramCountry + * + * @property int|null $program_id + * @property int|null $country_id + * @property-read \App\Models\TravelCountry|null $travel_country + * @property-read \App\Models\TravelProgram|null $travel_program + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramCountry whereCountryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramCountry whereProgramId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramCountry newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramCountry newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramCountry query() + * @mixin \Eloquent + */ + class TravelProgramCountry extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\TravelProgramDraft + * + * @property int $id + * @property int $travel_program_id + * @property int $travel_class_id + * @property int $draft_id + * @property array $weekdays + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\Draft $draft + * @property-read \App\Models\TravelClass $travel_class + * @property-read \App\Models\TravelProgram $travel_program + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft whereDraftId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft whereTravelClassId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft whereTravelProgramId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft whereWeekdays($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelProgramDraft query() + * @mixin \Eloquent + */ + class TravelProgramDraft extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelUser + * + * @property int $id + * @property int $salutation_id + * @property string $title + * @property string $first_name + * @property string $last_name + * @property string $email + * @property string $password + * @property string $company + * @property string $street + * @property string $zipcode + * @property string $city + * @property string $phone + * @property string $mobil + * @property string $fax + * @property \Carbon\Carbon $birthday + * @property int $travel_nationality_id + * @property string $last_user_data + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $deleted_at + * @property \App\Models\TravelNationality $travel_nationality + * @property \Illuminate\Database\Eloquent\Collection $travel_user_booking_fewos + * @package App\Models + * @method static bool|null forceDelete() + * @method static \Illuminate\Database\Query\Builder|\App\Models\TravelUser onlyTrashed() + * @method static bool|null restore() + * @method static \Illuminate\Database\Query\Builder|\App\Models\TravelUser withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\Models\TravelUser withoutTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereBirthday($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereCity($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereCompany($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereFax($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereFirstName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereLastName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereLastUserData($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereMobil($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser wherePassword($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser wherePhone($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereSalutationId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereStreet($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereTitle($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereTravelNationalityId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUser whereZipcode($value) + * @property-read int|null $travel_user_booking_fewos_count + * @mixin \Eloquent + */ + class TravelUser extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelUserBookingFewo + * + * @property int $id + * @property int $travel_user_id + * @property int $fewo_lodging_id + * @property string $invoice_number + * @property int $persons + * @property int $adults + * @property int $children + * @property \Carbon\Carbon $booking_date + * @property \Carbon\Carbon $from_date + * @property \Carbon\Carbon $to_date + * @property string $daily_prices + * @property float $price_travel + * @property float $price_deposit + * @property float $price_service + * @property float $price_total + * @property int $travel_booking_fewo_channel_id + * @property string $notice + * @property bool $is_calendar_fewo_direct + * @property bool $is_calendar_hrs + * @property bool $is_calendar_stern_tours + * @property int $status + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $deleted_at + * @property \App\Models\FewoLodging $fewo_lodging + * @property \App\Models\TravelBookingFewoChannel $travel_booking_fewo_channel + * @property \App\Models\TravelUser $travel_user + * @package App\Models + * @method static bool|null forceDelete() + * @method static \Illuminate\Database\Query\Builder|\App\Models\TravelUserBookingFewo onlyTrashed() + * @method static bool|null restore() + * @method static \Illuminate\Database\Query\Builder|\App\Models\TravelUserBookingFewo withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\Models\TravelUserBookingFewo withoutTrashed() + * @property int|null $fewo_reservation_id + * @property float|null $price_balance + * @property float|null $price_extra + * @property float|null $price_travel_total + * @property string|null $status_text + * @property array|null $send_user_mail + * @property array|null $send_service_mail + * @property array|null $send_info_mail + * @property array|null $send_employee_mail + * @property string|null $info_mail_text + * @property-read \App\Models\FewoReservation|null $fewo_reservation + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereAdults($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereBookingDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereChildren($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereDailyPrices($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereFewoLodgingId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereFewoReservationId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereFromDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereInfoMailText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereInvoiceNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereIsCalendarFewoDirect($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereIsCalendarHrs($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereIsCalendarSternTours($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereNotice($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePersons($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePriceBalance($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePriceDeposit($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePriceExtra($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePriceService($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePriceTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePriceTravel($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo wherePriceTravelTotal($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereSendEmployeeMail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereSendInfoMail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereSendServiceMail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereSendUserMail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereStatusText($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereToDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereTravelBookingFewoChannelId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereTravelUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereUpdatedAt($value) + * @property string|null $last_change_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFewo whereLastChangeAt($value) + * @property-read \App\Models\CustomerFewoMail|null $customer_fewo_mail_last + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\CustomerFewoMail[] $customer_fewo_mails + * @property-read int|null $customer_fewo_mails_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\CustomerFewoMail[] $customer_fewo_mails_sent_at + * @property-read int|null $customer_fewo_mails_sent_at_count + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelUserBookingFile[] $booking_files + * @property-read int|null $booking_files_count + * @property-read \App\Models\TravelUser $customer + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\TravelUserBookingFewoNotice[] $booking_fewo_notices + * @property-read int|null $booking_fewo_notices_count + * @property bool $is_calendar_traum_fewo + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewo whereIsCalendarTraumFewo($value) + * @mixin \Eloquent + */ + class TravelUserBookingFewo extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelUserBookingFewoNotice + * + * @property int $id + * @property int $travel_user_booking_fewo_id + * @property int $from_user_id + * @property int $to_user_id + * @property string $message + * @property bool $show + * @property bool $important + * @property Carbon $edit_at + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelUserBookingFewo $travel_user_booking_fewo + * @package App\Models + * @property-read User $from_user + * @property-read User|null $to_user + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice query() + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereEditAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereFromUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereImportant($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereShow($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereToUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereTravelUserBookingFewoId($value) + * @method static \Illuminate\Database\Eloquent\Builder|TravelUserBookingFewoNotice whereUpdatedAt($value) + * @mixin \Eloquent + */ + class TravelUserBookingFewoNotice extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class TravelUserBookingFile + * + * @property int $id + * @property int $travel_user_booking_fewos + * @property string $identifier + * @property string $filename + * @property string $dir + * @property string $original_name + * @property string $ext + * @property string $mine + * @property int $size + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TravelUserBookingFewo $travel_user_booking_fewo + * @package App\Models + * @property int|null $travel_user_booking_fewo_id + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereDir($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereOriginalName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereTravelUserBookingFewoId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\TravelUserBookingFile whereUpdatedAt($value) + * @mixin \Eloquent + */ + class TravelUserBookingFile extends \Eloquent {} +} + +namespace App\Models{ +/** + * App\Models\UserUpdateEmail + * + * @property-read \App\User $user + * @property int $user_id + * @property string $email + * @property string $token + * @property \Illuminate\Support\Carbon $created_at + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UserUpdateEmail newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UserUpdateEmail newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UserUpdateEmail query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UserUpdateEmail whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UserUpdateEmail whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UserUpdateEmail whereToken($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UserUpdateEmail whereUserId($value) + * @mixin \Eloquent + */ + class UserUpdateEmail extends \Eloquent {} +} + +namespace App\Models{ +/** + * Class Website + * + * @property int $id + * @property string $name + * @property Collection|Lead[] $leads + * @package App\Models + * @property-read int|null $leads_count + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Website newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Website newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Website query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Website whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Website whereName($value) + * @mixin \Eloquent + */ + class Website extends \Eloquent {} +} + +namespace App{ +/** + * App\User + * + * @property int $id + * @property string $name + * @property string $email + * @property string $password + * @property string|null $remember_token + * @property string|null $token + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User wherePassword($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRememberToken($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereToken($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereUpdatedAt($value) + * @property int|null $confirmed + * @property string|null $confirmation_code + * @property string|null $confirmation_date + * @property string|null $confirmation_code_to + * @property int|null $confirmation_code_remider + * @property int|null $active + * @property string|null $active_date + * @property string|null $agreement + * @property int|null $admin + * @property string|null $identify + * @property string|null $lang + * @property string|null $last_login + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property-read \App\Models\Account $account + * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UserUpdateEmail[] $user_update_email + * @method static bool|null forceDelete() + * @method static \Illuminate\Database\Query\Builder|\App\User onlyTrashed() + * @method static bool|null restore() + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereActiveDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereAdmin($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereAgreement($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereConfirmationCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereConfirmationCodeRemider($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereConfirmationCodeTo($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereConfirmationDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereConfirmed($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereIdentify($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereLang($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereLastLogin($value) + * @method static \Illuminate\Database\Query\Builder|\App\User withTrashed() + * @method static \Illuminate\Database\Query\Builder|\App\User withoutTrashed() + * @property-read \Illuminate\Database\Eloquent\Collection|\Laravel\Passport\Client[] $clients + * @property-read \Illuminate\Database\Eloquent\Collection|\Laravel\Passport\Token[] $tokens + * @property array|null $permissions + * @method static \Illuminate\Database\Eloquent\Builder|\App\User newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\User newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\User query() + * @method static \Illuminate\Database\Eloquent\Builder|\App\User wherePermissions($value) + * @property-read int|null $clients_count + * @property-read int|null $notifications_count + * @property-read int|null $tokens_count + * @property-read int|null $user_update_email_count + * @property-read \App\Models\SfGuardUser|null $sf_guard_user + * @property string|null $secret_key + * @property int|null $google2fa + * @method static \Illuminate\Database\Eloquent\Builder|User whereGoogle2fa($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereSecretKey($value) + * @mixin \Eloquent + */ + class User extends \Eloquent {} +} + +namespace IqContent\LaravelFilemanager\Models{ +/** + * IqContent\LaravelFilemanager\Models\IQContentCategory + * + * @property int $id + * @property string $name + * @property string $slug + * @property int $pos + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection|\IqContent\LaravelFilemanager\Models\IQContentTag[] $tags + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory findSimilarSlugs($attribute, $config, $slug) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory query() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory whereUpdatedAt($value) + * @property-read int|null $tags_count + * @property string|null $identifier + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentCategory whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|IQContentCategory withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class IQContentCategory extends \Eloquent {} +} + +namespace IqContent\LaravelFilemanager\Models{ +/** + * IqContent\LaravelFilemanager\Models\IQContentFile + * + * @property int $id + * @property int|null $folder_id + * @property string $name + * @property string|null $identifier + * @property string $slug + * @property string|null $ext + * @property string|null $mine + * @property int $size + * @property string|null $dimensions + * @property string|null $content + * @property int $color + * @property int $pos + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection|\IqContent\LaravelFilemanager\Models\IQContentFileTag[] $file_tags + * @property-read \IqContent\LaravelFilemanager\Models\IQContentFolder|null $folder + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile findSimilarSlugs($attribute, $config, $slug) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile query() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereColor($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereContent($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereDimensions($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereExt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereFolderId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereMine($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereSize($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFile whereUpdatedAt($value) + * @property-read int|null $file_tags_count + * @method static \Illuminate\Database\Eloquent\Builder|IQContentFile withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class IQContentFile extends \Eloquent {} +} + +namespace IqContent\LaravelFilemanager\Models{ +/** + * IqContent\LaravelFilemanager\Models\IQContentFileTag + * + * @property int $id + * @property int $file_id + * @property int $tag_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \IqContent\LaravelFilemanager\Models\IQContentFile $file + * @property-read \IqContent\LaravelFilemanager\Models\IQContentTag $tag + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag query() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag whereFileId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag whereTagId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFileTag whereUpdatedAt($value) + * @mixin \Eloquent + */ + class IQContentFileTag extends \Eloquent {} +} + +namespace IqContent\LaravelFilemanager\Models{ +/** + * IqContent\LaravelFilemanager\Models\IQContentFolder + * + * @property int $id + * @property int|null $folder_id + * @property string $name + * @property string $slug + * @property string|null $identifier + * @property string|null $path + * @property int $color + * @property int $pos + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection|\IqContent\LaravelFilemanager\Models\IQContentFile[] $files + * @property-read \IqContent\LaravelFilemanager\Models\IQContentFolder|null $folder + * @property-read \Illuminate\Database\Eloquent\Collection|\IqContent\LaravelFilemanager\Models\IQContentFolder[] $folders + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder findSimilarSlugs($attribute, $config, $slug) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder query() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereColor($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereFolderId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder wherePath($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentFolder whereUpdatedAt($value) + * @property-read int|null $files_count + * @property-read int|null $folders_count + * @method static \Illuminate\Database\Eloquent\Builder|IQContentFolder withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class IQContentFolder extends \Eloquent {} +} + +namespace IqContent\LaravelFilemanager\Models{ +/** + * IqContent\LaravelFilemanager\Models\IQContentTag + * + * @property int $id + * @property int $category_id + * @property string $name + * @property string $slug + * @property int $pos + * @property int $active + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \IqContent\LaravelFilemanager\Models\IQContentCategory $category + * @property-read \Illuminate\Database\Eloquent\Collection|\IqContent\LaravelFilemanager\Models\IQContentFileTag[] $file_tags + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag findSimilarSlugs($attribute, $config, $slug) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag query() + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag whereActive($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag whereCategoryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag wherePos($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|\IqContent\LaravelFilemanager\Models\IQContentTag whereUpdatedAt($value) + * @property-read int|null $file_tags_count + * @method static \Illuminate\Database\Eloquent\Builder|IQContentTag withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) + * @mixin \Eloquent + */ + class IQContentTag extends \Eloquent {} +} + diff --git a/app/Console/Commands/ContactsFindDuplicates.php b/app/Console/Commands/ContactsFindDuplicates.php new file mode 100644 index 0000000..35ab2fb --- /dev/null +++ b/app/Console/Commands/ContactsFindDuplicates.php @@ -0,0 +1,160 @@ +info('Suche nach Duplikaten in der customer-Tabelle...'); + $this->newLine(); + + $groups = collect(); + + // ── HIGH: gleiche E-Mail ────────────────────────────────────────── + if ($this->shouldCheck('HIGH')) { + $emailDupes = DB::table('contacts') + ->select('email', DB::raw('COUNT(*) as cnt'), DB::raw('GROUP_CONCAT(id ORDER BY updated_at DESC) as ids')) + ->whereNotNull('email') + ->where('email', '!=', '') + ->whereNull('merged_into_id') + ->groupBy('email') + ->having('cnt', '>', 1) + ->get(); + + foreach ($emailDupes as $row) { + $groups->push([ + 'confidence' => 'HIGH', + 'reason' => 'E-Mail: ' . $row->email, + 'ids' => $row->ids, + 'count' => $row->cnt, + ]); + } + + $this->line(sprintf('HIGH (gleiche E-Mail): %d Gruppen', $emailDupes->count())); + } + + // ── MEDIUM: Name + Vorname + Geburtsdatum ──────────────────────── + if ($this->shouldCheck('MEDIUM')) { + $nameBdDupes = DB::table('contacts') + ->select( + 'name', 'firstname', 'birthdate', + DB::raw('COUNT(*) as cnt'), + DB::raw('GROUP_CONCAT(id ORDER BY updated_at DESC) as ids') + ) + ->whereNotNull('name') + ->whereNotNull('firstname') + ->whereNotNull('birthdate') + ->whereNull('merged_into_id') + ->groupBy('name', 'firstname', 'birthdate') + ->having('cnt', '>', 1) + ->get(); + + foreach ($nameBdDupes as $row) { + $groups->push([ + 'confidence' => 'MEDIUM', + 'reason' => "Name: {$row->firstname} {$row->name}, GD: {$row->birthdate}", + 'ids' => $row->ids, + 'count' => $row->cnt, + ]); + } + + $this->line(sprintf('MEDIUM (Name+GD): %d Gruppen', $nameBdDupes->count())); + } + + // ── LOW: Name + Vorname + PLZ ───────────────────────────────────── + if ($this->shouldCheck('LOW')) { + $nameZipDupes = DB::table('contacts') + ->select( + 'name', 'firstname', 'zip', + DB::raw('COUNT(*) as cnt'), + DB::raw('GROUP_CONCAT(id ORDER BY updated_at DESC) as ids') + ) + ->whereNotNull('name') + ->whereNotNull('firstname') + ->whereNotNull('zip') + ->where('zip', '!=', '') + ->whereNull('merged_into_id') + ->groupBy('name', 'firstname', 'zip') + ->having('cnt', '>', 1) + ->get(); + + foreach ($nameZipDupes as $row) { + $groups->push([ + 'confidence' => 'LOW', + 'reason' => "Name: {$row->firstname} {$row->name}, PLZ: {$row->zip}", + 'ids' => $row->ids, + 'count' => $row->cnt, + ]); + } + + $this->line(sprintf('LOW (Name+PLZ): %d Gruppen', $nameZipDupes->count())); + } + + $this->newLine(); + $this->info(sprintf('Gesamt: %d Duplikat-Gruppen gefunden', $groups->count())); + + if ($groups->isEmpty()) { + $this->info('Keine Duplikate — nichts zu tun.'); + return self::SUCCESS; + } + + // Tabellen-Ausgabe + $this->table( + ['Konfidenz', 'Grund', 'IDs (neueste zuerst)', 'Anzahl'], + $groups->map(fn ($g) => [$g['confidence'], $g['reason'], $g['ids'], $g['count']])->all() + ); + + // CSV-Export + if ($export = $this->option('export')) { + $this->exportCsv($groups->all(), $export); + $this->info("CSV gespeichert: {$export}"); + } else { + $this->newLine(); + $this->line('Tipp: --export=duplicates.csv für CSV-Export'); + $this->line('Tipp: php artisan contacts:merge-duplicates --dry-run zum Prüfen'); + } + + return self::SUCCESS; + } + + private function shouldCheck(string $level): bool + { + $filter = strtoupper((string) $this->option('confidence')); + return $filter === '' || $filter === $level; + } + + private function exportCsv(array $groups, string $path): void + { + $handle = fopen($path, 'w'); + fputcsv($handle, ['Konfidenz', 'Grund', 'IDs (neueste zuerst)', 'Anzahl']); + foreach ($groups as $group) { + fputcsv($handle, [$group['confidence'], $group['reason'], $group['ids'], $group['count']]); + } + fclose($handle); + } +} diff --git a/app/Console/Commands/ContactsMergeDuplicates.php b/app/Console/Commands/ContactsMergeDuplicates.php new file mode 100644 index 0000000..9aeeca6 --- /dev/null +++ b/app/Console/Commands/ContactsMergeDuplicates.php @@ -0,0 +1,233 @@ +dryRun = (bool) $this->option('dry-run'); + + if ($this->dryRun) { + $this->warn('DRY-RUN Modus — keine Daten werden verändert'); + } else { + $this->warn('ACHTUNG: Diese Operation verändert Produktionsdaten.'); + if (!$this->option('force') && !$this->confirm('Fortfahren?')) { + $this->info('Abgebrochen.'); + return self::SUCCESS; + } + } + + $this->newLine(); + + DB::transaction(function () { + $this->processLevel( + 'HIGH', + fn () => $this->findByEmail() + ); + + $this->processLevel( + 'MEDIUM', + fn () => $this->findByNameBirthdate() + ); + + $this->processLevel( + 'LOW', + fn () => $this->findByNameZip() + ); + }); + + $this->newLine(); + $this->info(sprintf( + '%s %d Duplikate zusammengeführt | %d leads aktualisiert | %d bookings aktualisiert', + $this->dryRun ? '[DRY-RUN]' : '', + $this->mergedCount, + $this->updatedLeads, + $this->updatedBookings + )); + + if ($this->dryRun) { + $this->newLine(); + $this->line('Zum Ausführen: php artisan contacts:merge-duplicates'); + } + + return self::SUCCESS; + } + + // ───────────────────────────────────────────────────────────────────────── + // Duplikat-Gruppen ermitteln + // ───────────────────────────────────────────────────────────────────────── + + private function findByEmail(): array + { + return DB::table('contacts') + ->select('email', DB::raw('GROUP_CONCAT(id ORDER BY updated_at DESC, id DESC) as ids')) + ->whereNotNull('email') + ->where('email', '!=', '') + ->whereNull('merged_into_id') + ->groupBy('email') + ->having(DB::raw('COUNT(*)'), '>', 1) + ->pluck('ids') + ->map(fn ($ids) => explode(',', $ids)) + ->all(); + } + + private function findByNameBirthdate(): array + { + return DB::table('contacts') + ->select(DB::raw('GROUP_CONCAT(id ORDER BY updated_at DESC, id DESC) as ids')) + ->whereNotNull('name') + ->whereNotNull('firstname') + ->whereNotNull('birthdate') + ->whereNull('merged_into_id') + ->groupBy('name', 'firstname', 'birthdate') + ->having(DB::raw('COUNT(*)'), '>', 1) + ->pluck('ids') + ->map(fn ($ids) => explode(',', $ids)) + ->all(); + } + + private function findByNameZip(): array + { + return DB::table('contacts') + ->select(DB::raw('GROUP_CONCAT(id ORDER BY updated_at DESC, id DESC) as ids')) + ->whereNotNull('name') + ->whereNotNull('firstname') + ->whereNotNull('zip') + ->where('zip', '!=', '') + ->whereNull('merged_into_id') + ->groupBy('name', 'firstname', 'zip') + ->having(DB::raw('COUNT(*)'), '>', 1) + ->pluck('ids') + ->map(fn ($ids) => explode(',', $ids)) + ->all(); + } + + // ───────────────────────────────────────────────────────────────────────── + // Verarbeitung + // ───────────────────────────────────────────────────────────────────────── + + private function processLevel(string $level, callable $finder): void + { + if ($filter = $this->option('confidence')) { + if (strtoupper($filter) !== $level) { + return; + } + } + + $groups = $finder(); + + if (empty($groups)) { + $this->line("[{$level}] Keine Duplikate."); + return; + } + + $this->line(sprintf('[%s] %d Gruppe(n) gefunden', $level, count($groups))); + + foreach ($groups as $ids) { + $masterId = (int) $ids[0]; // erster = neuester + $duplicateIds = array_map('intval', array_slice($ids, 1)); + + $this->line(sprintf( + ' Master: #%d ← Duplikate: %s', + $masterId, + implode(', ', array_map(fn ($id) => '#' . $id, $duplicateIds)) + )); + + foreach ($duplicateIds as $dupeId) { + $this->mergeInto($masterId, $dupeId); + } + } + } + + private function mergeInto(int $masterId, int $dupeId): void + { + // 1. Leads umhängen + $leadCount = DB::table('inquiries')->where('customer_id', $dupeId)->count(); + if ($leadCount > 0) { + $this->line(" lead.customer_id: {$leadCount} Zeile(n) → #{$masterId}"); + if (!$this->dryRun) { + DB::table('inquiries') + ->where('customer_id', $dupeId) + ->update(['customer_id' => $masterId]); + } + $this->updatedLeads += $leadCount; + } + + // 2. Bookings umhängen + $bookingCount = DB::table('booking')->where('customer_id', $dupeId)->count(); + if ($bookingCount > 0) { + $this->line(" booking.customer_id: {$bookingCount} Zeile(n) → #{$masterId}"); + if (!$this->dryRun) { + DB::table('booking') + ->where('customer_id', $dupeId) + ->update(['customer_id' => $masterId]); + } + $this->updatedBookings += $bookingCount; + } + + // 3. customer_mails umhängen + $mailCount = DB::table('customer_mails')->where('customer_id', $dupeId)->count(); + if ($mailCount > 0) { + $this->line(" customer_mails.customer_id: {$mailCount} Zeile(n) → #{$masterId}"); + if (!$this->dryRun) { + DB::table('customer_mails') + ->where('customer_id', $dupeId) + ->update(['customer_id' => $masterId]); + } + } + + // 4. lead_mails umhängen + $leadMailCount = DB::table('lead_mails')->where('customer_id', $dupeId)->count(); + if ($leadMailCount > 0) { + $this->line(" lead_mails.customer_id: {$leadMailCount} Zeile(n) → #{$masterId}"); + if (!$this->dryRun) { + DB::table('lead_mails') + ->where('customer_id', $dupeId) + ->update(['customer_id' => $masterId]); + } + } + + // 5. Duplikat als zusammengeführt markieren + if (!$this->dryRun) { + DB::table('contacts') + ->where('id', $dupeId) + ->update([ + 'merged_into_id' => $masterId, + 'merged_at' => now(), + ]); + } + + $this->mergedCount++; + } +} diff --git a/app/Console/Commands/SyncNewsletterKulturreisen.php b/app/Console/Commands/SyncNewsletterKulturreisen.php index c895505..a6c5fbc 100644 --- a/app/Console/Commands/SyncNewsletterKulturreisen.php +++ b/app/Console/Commands/SyncNewsletterKulturreisen.php @@ -193,7 +193,8 @@ class SyncNewsletterKulturreisen extends Command 'description' => 'Kontakt durch Kulturreisen-Buchung erstellt', 'metadata' => [ 'booking_id' => $booking->id, - 'lead_id' => $booking->lead_id, + // Metadaten-Key bleibt `lead_id`; Wert kommt aus booking.inquiry_id. + 'lead_id' => $booking->inquiry_id, ], ]); } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index a8c5158..4a5f9fc 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -22,10 +22,14 @@ class Kernel extends ConsoleKernel * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { - // $schedule->command('inspire') - // ->hourly(); + // Duplikate täglich um 02:00 Uhr automatisch zusammenführen (nur HIGH-Konfidenz = exakte E-Mail-Treffer) + $schedule->command('contacts:merge-duplicates --confidence=HIGH --force') + ->dailyAt('02:00') + ->withoutOverlapping() + ->runInBackground() + ->appendOutputTo(storage_path('logs/contacts-merge.log')); } /** @@ -35,7 +39,7 @@ class Kernel extends ConsoleKernel */ protected function commands() { - $this->load(__DIR__.'/Commands'); + $this->load(__DIR__ . '/Commands'); require base_path('routes/console.php'); } diff --git a/app/Http/Controllers/API/BookingController.php b/app/Http/Controllers/API/BookingController.php index e47cd07..25b6c91 100755 --- a/app/Http/Controllers/API/BookingController.php +++ b/app/Http/Controllers/API/BookingController.php @@ -8,42 +8,37 @@ use App\Services\BookingImport; class BookingController extends Controller { - private $successStatus = 200; - private $successKey = 'f6077389c9ce710e554763a5de02c8ec'; - - protected $draftRepo; + private int $successStatus = 200; + private string $successKey; public function __construct() { - + $this->successKey = config('app.success_key'); } public function import() { $request = \Request::all(); - if(!isset($request['key']) || $request['key'] !== $this->successKey){ + if (!isset($request['key']) || $request['key'] !== $this->successKey) { return response()->json(['error' => "key"], 401); } $travel_booking = TravelBooking::find($request['travel_booking_id']); - //# vor testing - //$travel_booking = TravelBooking::find(2922); - if(!isset($travel_booking) || !$travel_booking){ + if (!$travel_booking) { return response()->json(['error' => 'no-booking-found'], $this->successStatus); } $booking = BookingImport::importFrom($travel_booking); - $ret= [ - 'url_v1' => make_old_url('/index.php/booking/'.$booking->id.'/edit'), + $ret = [ + 'url_v1' => make_old_url('/index.php/booking/' . $booking->id . '/edit'), 'url_v3' => route('booking_detail', $booking->id), - 'lead_id' => $booking->lead_id + // API-Feld bleibt `lead_id` aus Abwärtskompatibilität für API-Konsumenten; + // Wert kommt nach Modul 3 Phase 2 aus booking.inquiry_id. + 'lead_id' => $booking->inquiry_id ]; return response()->json(['success' => "import", "ret" => $ret], $this->successStatus); - //return response()->json(['error' => 'no-node'], $this->successStatus); } - - } diff --git a/app/Http/Controllers/Admin/ReportController.php b/app/Http/Controllers/Admin/ReportController.php index 7b46918..ab45f3d 100755 --- a/app/Http/Controllers/Admin/ReportController.php +++ b/app/Http/Controllers/Admin/ReportController.php @@ -400,10 +400,10 @@ class ReportController extends Controller $price = 0; $isset = []; foreach ($all as $v){ - if(!in_array($v->booking->lead_id, $isset)){ + if(!in_array($v->booking->inquiry_id, $isset)){ $price += $v->booking->isCanceled() ? $v->booking->getPriceCanceledRaw() : $v->booking->getPriceRaw(); } - $isset[] = $v->booking->lead_id; + $isset[] = $v->booking->inquiry_id; } return Util::_number_format($price); @@ -416,8 +416,8 @@ class ReportController extends Controller $price = 0; $isset = []; foreach ($all as $v){ - $price += in_array($v->booking->lead_id, $isset) ? 0 : $v->booking->getPriceTotalRaw(); - $isset[] = $v->booking->lead_id; + $price += in_array($v->booking->inquiry_id, $isset) ? 0 : $v->booking->getPriceTotalRaw(); + $isset[] = $v->booking->inquiry_id; } return Util::_number_format($price); }) @@ -429,8 +429,8 @@ class ReportController extends Controller $proceeds = 0; $isset = []; foreach ($all as $v){ - $proceeds += in_array($v->booking->lead_id, $isset) ? 0 : $v->booking->proceeds(true); - $isset[] = $v->booking->lead_id; + $proceeds += in_array($v->booking->inquiry_id, $isset) ? 0 : $v->booking->proceeds(true); + $isset[] = $v->booking->inquiry_id; } return Util::_number_format($proceeds); /*$all = $query->get(); @@ -562,7 +562,7 @@ class ReportController extends Controller $ctemps[$export->booking->id][] = array( 'Zähler' => $new ? $export->getCounter() : "", 'MyJack Nr.' => $new ? $export->booking->merlin_order_number : "", - 'CRM Nr' => $new ? $export->booking->lead_id : "", + 'CRM Nr' => $new ? $export->booking->inquiry_id : "", 'Kunde' => $new ? $export->booking->customer->name : "", 'Reisedatum' => $new ? $export->booking->getStartDateFormat() : "", 'Organisation' => $new ? ($export->booking->isCanceled() ? $export->booking->price_canceled : $export->booking->price) : "", @@ -630,7 +630,7 @@ class ReportController extends Controller $ctemps[$export->booking->id][] = array( 'Zähler' => $new ? $export->getCounter() : "", 'MyJack Nr.' => $new ? $export->booking->merlin_order_number : "", - 'CRM Nr' => $new ? $export->booking->lead_id : "", + 'CRM Nr' => $new ? $export->booking->inquiry_id : "", 'Kunde' => $new ? $export->booking->customer->name : "", 'Reisedatum' => $new ? $export->booking->getStartDateFormat() : "", 'Reiseland' => $new && $export->booking->travel_country ? $export->booking->travel_country->name : "", diff --git a/app/Http/Controllers/Admin/ReportLeadsController.php b/app/Http/Controllers/Admin/ReportLeadsController.php index 2f2b888..ddfe25a 100644 --- a/app/Http/Controllers/Admin/ReportLeadsController.php +++ b/app/Http/Controllers/Admin/ReportLeadsController.php @@ -49,7 +49,7 @@ class ReportLeadsController extends Controller { $query = Lead::with('customer')->with('sf_guard_user')->with('status') - ->select('lead.*'); + ->select('inquiries.*'); //->where('deleted_at', '=', null); /* @@ -132,7 +132,7 @@ class ReportLeadsController extends Controller // $q->select('sent_at')->where('sent_at', DB::raw("(select max('sent_at') customer_mails)")); //) })->orderBy( LeadMail::select('sent_at') - ->whereColumn('lead_id', 'lead.id') + ->whereColumn('lead_id', 'inquiries.id') ->orderBy('sent_at', 'DESC') ->limit(1) , $order); diff --git a/app/Http/Controllers/Admin/ReportProviderController.php b/app/Http/Controllers/Admin/ReportProviderController.php index 297a96a..d9dbf9c 100644 --- a/app/Http/Controllers/Admin/ReportProviderController.php +++ b/app/Http/Controllers/Admin/ReportProviderController.php @@ -104,10 +104,10 @@ class ReportProviderController extends Controller $price = 0; $isset = []; foreach ($all as $v){ - if(!in_array($v->booking->lead_id, $isset)){ + if(!in_array($v->booking->inquiry_id, $isset)){ $price += $v->booking->isCanceled() ? $v->booking->getPriceCanceledRaw() : $v->booking->getPriceRaw(); } - $isset[] = $v->booking->lead_id; + $isset[] = $v->booking->inquiry_id; } return Util::_number_format($price); @@ -120,8 +120,8 @@ class ReportProviderController extends Controller $price = 0; $isset = []; foreach ($all as $v){ - $price += in_array($v->booking->lead_id, $isset) ? 0 : $v->booking->getPriceTotalRaw(); - $isset[] = $v->booking->lead_id; + $price += in_array($v->booking->inquiry_id, $isset) ? 0 : $v->booking->getPriceTotalRaw(); + $isset[] = $v->booking->inquiry_id; } return Util::_number_format($price); }) @@ -133,8 +133,8 @@ class ReportProviderController extends Controller $proceeds = 0; $isset = []; foreach ($all as $v){ - $proceeds += in_array($v->booking->lead_id, $isset) ? 0 : $v->booking->proceeds(true); - $isset[] = $v->booking->lead_id; + $proceeds += in_array($v->booking->inquiry_id, $isset) ? 0 : $v->booking->proceeds(true); + $isset[] = $v->booking->inquiry_id; } return Util::_number_format($proceeds); /*$all = $query->get(); @@ -272,7 +272,7 @@ class ReportProviderController extends Controller $ctemps[$export->booking->id][] = array( 'Zähler' => $new ? $export->getCounter() : "", 'MyJack Nr.' => $new ? $export->booking->merlin_order_number : "", - 'CRM Nr' => $new ? $export->booking->lead_id : "", + 'CRM Nr' => $new ? $export->booking->inquiry_id : "", 'Kunde' => $new ? $export->booking->customer->name : "", 'Reisedatum' => $new ? $export->booking->getStartDateFormat() : "", 'bis' => $new ? $export->booking->getEndDateFormat() : "", @@ -346,7 +346,7 @@ class ReportProviderController extends Controller $ctemps[$export->booking->id][] = array( 'Zähler' => $new ? $export->getCounter() : "", 'MyJack Nr.' => $new ? $export->booking->merlin_order_number : "", - 'CRM Nr' => $new ? $export->booking->lead_id : "", + 'CRM Nr' => $new ? $export->booking->inquiry_id : "", 'Kunde' => $new ? $export->booking->customer->name : "", 'Reisedatum' => $new ? $export->booking->getStartDateFormat() : "", 'bis' => $new ? $export->booking->getEndDateFormat() : "", diff --git a/app/Http/Controllers/BookingController.php b/app/Http/Controllers/BookingController.php index 23ee3eb..7f5a914 100755 --- a/app/Http/Controllers/BookingController.php +++ b/app/Http/Controllers/BookingController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers; -use Request; +use Illuminate\Http\Request; use App\Models\Booking; use App\Models\BookingFile; use App\Models\Participant; @@ -60,11 +60,9 @@ class BookingController extends Controller return view('booking.detail', $data); } - public function store($id) + public function store(Request $request, $id) { - // \Session()->flash('alert-save', '1'); - - $data = Request::all(); + $data = $request->all(); if ($id === "new") { $booking = new Booking(); @@ -308,11 +306,11 @@ class BookingController extends Controller return $redirect; } - public function loadModal() + public function loadModal(Request $request) { - $data = Request::all(); + $data = $request->all(); $ret = ""; - if (Request::ajax()) { + if ($request->ajax()) { if ($data['action'] === "new-customer-mail" || $data['action'] === "reply-customer-mail" || $data['action'] === "show-customer-mail" || $data['action'] === "edit-customer-mail") { $data['customers'] = []; if ($data['action'] === "new-customer-mail" && isset($data['booking_id']) && $booking = Booking::find($data['booking_id'])) { @@ -374,7 +372,7 @@ class BookingController extends Controller $bookingFileRepo->_set('booking_id', $data['booking_id']); $bookingFileRepo->_set('dir', '/files/' . date('Y/m') . '/'); $bookingFileRepo->_set('identifier', 'booking'); - return $bookingFileRepo->uploadFile(Request::all()); + return $bookingFileRepo->uploadFile($request->all()); } } } @@ -391,7 +389,7 @@ class BookingController extends Controller return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation"); } - public function action($action, $id = false) + public function action(Request $request, $action, $id = false) { if (!$booking = Booking::find($id)) { @@ -400,7 +398,7 @@ class BookingController extends Controller if ($action === 'change_travel_dates') { $draftRepo = new DraftRepository($booking); - $draftRepo->change_dates_drafts_from_booking(Request::get('change_travel_start_date')); + $draftRepo->change_dates_drafts_from_booking($request->get('change_travel_start_date')); \Session()->flash('alert-success', __('Datum der Reise wurde geändert')); return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation"); } diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php new file mode 100644 index 0000000..effb97d --- /dev/null +++ b/app/Http/Controllers/ContactController.php @@ -0,0 +1,312 @@ +middleware(['admin', '2fa']); + } + + public function index(): \Illuminate\View\View + { + return view('contact.index'); + } + + public function detail(string $id): \Illuminate\View\View + { + if ($id === 'new') { + $contact = new Contact(); + } else { + $contact = Contact::with(['salutation', 'leads', 'bookings', 'mergedContacts']) + ->findOrFail((int) $id); + } + + return view('contact.detail', [ + 'contact' => $contact, + 'id' => $id, + ]); + } + + public function store(Request $request, string $id): \Illuminate\Http\RedirectResponse + { + $data = $request->except('_token'); + + if (!isset($data['action'])) { + abort(403, 'keine Action'); + } + + if ($id === 'new') { + $contact = $this->contactRepo->createContact($data); + \Session()->flash('alert-save', '1'); + return redirect(route('contact_detail', [$contact->id]) . '#collapseContactDetail'); + } + + $this->contactRepo->updateContact($id, $data); + \Session()->flash('alert-save', '1'); + + return redirect(route('contact_detail', [$id]) . '#collapseContactDetail'); + } + + public function history(int $id): \Illuminate\View\View + { + $contact = Contact::with([ + 'leads.sf_guard_user', + 'bookings.travel_country', + 'bookings.travel_agenda', + 'bookings.sf_guard_user', + 'bookings.lead', + ])->findOrFail($id); + + return view('contact._detail_history', [ + 'contact' => $contact, + 'modal' => true, + ]); + } + + public function destroy(int $id): \Illuminate\Http\JsonResponse + { + $contact = Contact::withoutGlobalScope('not_merged')->findOrFail($id); + + $leadsCount = $contact->leads()->count(); + $bookingCount = $contact->bookings()->count(); + + if ($leadsCount > 0 || $bookingCount > 0) { + return response()->json([ + 'success' => false, + 'message' => sprintf( + 'Kontakt kann nicht gelöscht werden: %s%s vorhanden.', + $leadsCount > 0 ? $leadsCount . ' Anfrage(n)' : '', + $bookingCount > 0 ? ($leadsCount > 0 ? ' und ' : '') . $bookingCount . ' Buchung(en)' : '' + ), + ], 422); + } + + $contact->delete(); + + return response()->json(['success' => true]); + } + + public function duplicates(): \Illuminate\View\View + { + $counts = [ + 'HIGH' => $this->countDuplicateGroups('email'), + 'MEDIUM' => $this->countDuplicateGroups('name_birthdate'), + 'LOW' => $this->countDuplicateGroups('name_zip'), + ]; + + return view('contact.duplicates', compact('counts')); + } + + public function getDuplicateGroups(Request $request): \Illuminate\Http\JsonResponse + { + $confidence = strtoupper($request->input('confidence', 'HIGH')); + + $groups = match ($confidence) { + 'HIGH' => $this->findByEmail(), + 'MEDIUM' => $this->findByNameBirthdate(), + 'LOW' => $this->findByNameZip(), + default => [], + }; + + // Für jede Gruppe die vollständigen Kontakt-Daten laden + $result = []; + foreach ($groups as $ids) { + $contacts = Contact::withoutGlobalScopes() + ->withCount(['leads', 'bookings']) + ->whereIn('id', $ids) + ->whereNull('merged_into_id') + ->whereNull('deleted_at') + ->orderByRaw('FIELD(id, ' . implode(',', $ids) . ')') + ->get(['id', 'firstname', 'name', 'email', 'zip', 'city', 'phone', 'phonemobile', 'birthdate', 'created_at', 'updated_at']); + + if ($contacts->count() < 2) { + continue; + } + + $result[] = [ + 'master' => $contacts->first(), + 'duplicates' => $contacts->skip(1)->values(), + ]; + } + + return response()->json($result); + } + + public function merge(Request $request): \Illuminate\Http\JsonResponse + { + $masterId = (int) $request->input('master_id'); + $dupeId = (int) $request->input('duplicate_id'); + + if (!$masterId || !$dupeId || $masterId === $dupeId) { + return response()->json(['success' => false, 'message' => 'Ungültige IDs.'], 422); + } + + $master = Contact::withoutGlobalScopes()->find($masterId); + $dupe = Contact::withoutGlobalScopes()->find($dupeId); + + if (!$master || !$dupe) { + return response()->json(['success' => false, 'message' => 'Kontakt nicht gefunden.'], 404); + } + + DB::transaction(function () use ($masterId, $dupeId) { + DB::table('inquiries')->where('customer_id', $dupeId)->update(['customer_id' => $masterId]); + DB::table('booking')->where('customer_id', $dupeId)->update(['customer_id' => $masterId]); + DB::table('customer_mails')->where('customer_id', $dupeId)->update(['customer_id' => $masterId]); + DB::table('lead_mails')->where('customer_id', $dupeId)->update(['customer_id' => $masterId]); + DB::table('contacts')->where('id', $dupeId)->update([ + 'merged_into_id' => $masterId, + 'merged_at' => now(), + ]); + }); + + return response()->json(['success' => true]); + } + + // ── Duplikat-Hilfs-Queries ──────────────────────────────────────────────── + + private function countDuplicateGroups(string $type): int + { + return match ($type) { + 'email' => DB::table('contacts')->selectRaw('COUNT(*) as cnt')->whereNotNull('email')->where('email', '!=', '')->whereNull('merged_into_id')->whereNull('deleted_at')->groupBy('email')->havingRaw('COUNT(*) > 1')->get()->count(), + 'name_birthdate' => DB::table('contacts')->selectRaw('COUNT(*) as cnt')->whereNotNull('name')->whereNotNull('firstname')->whereNotNull('birthdate')->whereNull('merged_into_id')->whereNull('deleted_at')->groupBy('name', 'firstname', 'birthdate')->havingRaw('COUNT(*) > 1')->get()->count(), + 'name_zip' => DB::table('contacts')->selectRaw('COUNT(*) as cnt')->whereNotNull('name')->whereNotNull('firstname')->whereNotNull('zip')->where('zip', '!=', '')->whereNull('merged_into_id')->whereNull('deleted_at')->groupBy('name', 'firstname', 'zip')->havingRaw('COUNT(*) > 1')->get()->count(), + default => 0, + }; + } + + private function findByEmail(): array + { + return DB::table('contacts') + ->selectRaw('GROUP_CONCAT(id ORDER BY updated_at DESC, id DESC) as ids') + ->whereNotNull('email')->where('email', '!=', '') + ->whereNull('merged_into_id')->whereNull('deleted_at') + ->groupBy('email')->havingRaw('COUNT(*) > 1') + ->pluck('ids')->map(fn ($s) => array_map('intval', explode(',', $s)))->all(); + } + + private function findByNameBirthdate(): array + { + return DB::table('contacts') + ->selectRaw('GROUP_CONCAT(id ORDER BY updated_at DESC, id DESC) as ids') + ->whereNotNull('name')->whereNotNull('firstname')->whereNotNull('birthdate') + ->whereNull('merged_into_id')->whereNull('deleted_at') + ->groupBy('name', 'firstname', 'birthdate')->havingRaw('COUNT(*) > 1') + ->pluck('ids')->map(fn ($s) => array_map('intval', explode(',', $s)))->all(); + } + + private function findByNameZip(): array + { + return DB::table('contacts') + ->selectRaw('GROUP_CONCAT(id ORDER BY updated_at DESC, id DESC) as ids') + ->whereNotNull('name')->whereNotNull('firstname') + ->whereNotNull('zip')->where('zip', '!=', '') + ->whereNull('merged_into_id')->whereNull('deleted_at') + ->groupBy('name', 'firstname', 'zip')->havingRaw('COUNT(*) > 1') + ->pluck('ids')->map(fn ($s) => array_map('intval', explode(',', $s)))->all(); + } + + public function restore(int $id): \Illuminate\Http\JsonResponse + { + $contact = Contact::onlyTrashed()->withoutGlobalScope('not_merged')->findOrFail($id); + $contact->restore(); + + return response()->json(['success' => true]); + } + + public function getContacts(Request $request): \Illuminate\Http\JsonResponse + { + $showDeleted = $request->filled('filter_deleted'); + + // Reihenfolge wichtig: select() zuerst, dann withCount() — sonst überschreibt + // select() die COUNT-Subqueries die withCount() per addSelect() eingetragen hat. + $query = $showDeleted + ? Contact::onlyTrashed()->withoutGlobalScope('not_merged')->select('contacts.*')->withCount(['leads', 'bookings']) + : Contact::select('contacts.*')->withCount(['leads', 'bookings']); + + // Zusatzfilter aus der UI (werden als extra GET-Parameter gesendet) + if ($request->filled('filter_has_leads')) { + $query->has('leads'); + } + if ($request->filled('filter_has_bookings')) { + $query->has('bookings'); + } + if ($request->filled('filter_has_email')) { + $query->whereNotNull('email')->where('email', '!=', ''); + } + + return DataTables::eloquent($query) + ->addColumn('action_edit', function (Contact $contact) use ($showDeleted) { + if ($showDeleted) { + return ''; + } + return ''; + }) + ->addColumn('action_delete', function (Contact $contact) use ($showDeleted) { + if ($showDeleted) { + return ''; + } + return ''; + }) + ->addColumn('id', function (Contact $contact) use ($showDeleted) { + if ($showDeleted) { + return '' . $contact->id . ''; + } + return '' . $contact->id . ''; + }) + ->addColumn('raw_id', fn(Contact $contact) => $contact->id) + ->addColumn('leads_count', fn(Contact $contact) => $contact->leads_count) + ->addColumn('bookings_count', fn(Contact $contact) => $contact->bookings_count) + ->addColumn('deleted_at', fn(Contact $contact) => $contact->deleted_at?->format('d.m.Y H:i') ?? '') + ->orderColumn('id', 'customer.id $1') + ->orderColumn('deleted_at', 'customer.deleted_at $1') + ->filterColumn('id', function ($query, $keyword) { + if ($keyword !== '') { + $query->where('contacts.id', 'LIKE', '%' . $keyword . '%'); + } + }) + ->filterColumn('name', function ($query, $keyword) { + if ($keyword !== '') { + $query->where(function ($q) use ($keyword) { + $q->where('name', 'LIKE', '%' . $keyword . '%') + ->orWhere('firstname', 'LIKE', '%' . $keyword . '%'); + }); + } + }) + ->filter(function ($query) use ($request) { + $location = $request->input('filter_location'); + if ($location && $location !== '') { + $query->where(function ($q) use ($location) { + $q->where('zip', 'LIKE', '%' . $location . '%') + ->orWhere('city', 'LIKE', '%' . $location . '%'); + }); + } + + $search = $request->input('search.value'); + if ($search && $search !== '') { + $query->where(function ($q) use ($search) { + $q->where('name', 'LIKE', '%' . $search . '%') + ->orWhere('firstname', 'LIKE', '%' . $search . '%') + ->orWhere('email', 'LIKE', '%' . $search . '%') + ->orWhere('phone', 'LIKE', '%' . $search . '%') + ->orWhere('phonemobile', 'LIKE', '%' . $search . '%'); + }); + } + }, true) + ->rawColumns(['action_edit', 'action_delete', 'id']) + ->make(true); + } +} diff --git a/app/Http/Controllers/CustomerController.php b/app/Http/Controllers/CustomerController.php index f52fb12..fc149ed 100755 --- a/app/Http/Controllers/CustomerController.php +++ b/app/Http/Controllers/CustomerController.php @@ -68,7 +68,7 @@ class CustomerController extends Controller public function getCustomers() { - $query = Customer::with('salutation')->select('customer.*'); + $query = Customer::with('salutation')->select('contacts.*'); return \DataTables::eloquent($query) ->addColumn('action_edit', function (Customer $customer) { diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 766704e..0ea5796 100755 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -14,15 +14,6 @@ use Request; class HomeController extends Controller { - /** - * Create a new controller instance. - * - * @return void - */ - public function __construct() - { - } - /** * Show the application dashboard. * diff --git a/app/Http/Controllers/LeadController.php b/app/Http/Controllers/LeadController.php index 60d7202..a4d99fb 100755 --- a/app/Http/Controllers/LeadController.php +++ b/app/Http/Controllers/LeadController.php @@ -232,7 +232,7 @@ class LeadController extends Controller public function getLeads() { - $query = Lead::with('customer')->with('sf_guard_user')->with('status')->select('lead.*'); + $query = Lead::with('customer')->with('sf_guard_user')->with('status')->select('inquiries.*'); return \DataTables::eloquent($query) ->addColumn('action_edit', function (Lead $lead) { @@ -296,7 +296,7 @@ class LeadController extends Controller // $q->select('sent_at')->where('sent_at', DB::raw("(select max('sent_at') customer_mails)")); //) })->orderBy( LeadMail::select('sent_at') - ->whereColumn('lead_id', 'lead.id') + ->whereColumn('lead_id', 'inquiries.id') ->orderBy('sent_at', 'DESC') ->limit(1) , $order); diff --git a/app/Http/Controllers/RequestController.php b/app/Http/Controllers/RequestController.php index a7f21e5..a02f566 100755 --- a/app/Http/Controllers/RequestController.php +++ b/app/Http/Controllers/RequestController.php @@ -89,7 +89,7 @@ class RequestController extends Controller wirte old where has state to new has travel_documents $bs = Booking::whereHas('arrangements', function($q){ $q->where('state', '!=', NULL); - })->where('lead_id', '!=', NULL)->where('new_drafts', 0)->get(); + })->where('inquiry_id', '!=', NULL)->where('new_drafts', 0)->get(); foreach ($bs as $b){ $b->travel_documents = true; @@ -101,7 +101,7 @@ class RequestController extends Controller private function getSearchRequests() { - $query = Booking::with('lead')->with('customer')->with('customer_mails')->with('customer_mails')->select('booking.*')->where('lead_id', '!=', NULL); + $query = Booking::with('lead')->with('customer')->with('customer_mails')->with('customer_mails')->select('booking.*')->where('inquiry_id', '!=', NULL); if (Request::get('full_firstname_search') != "") { $query->whereHas('customer', function ($q) { @@ -241,7 +241,7 @@ class RequestController extends Controller } if (Request::get('full_lead_id_search') != "") { - $query->where('lead_id', 'LIKE', '%' . Request::get('full_lead_id_search') . '%'); + $query->where('inquiry_id', 'LIKE', '%' . Request::get('full_lead_id_search') . '%'); } if (Request::get('full_booking_id_search') != "") { $query->where('id', 'LIKE', '%' . Request::get('full_booking_id_search') . '%'); @@ -398,10 +398,10 @@ class RequestController extends Controller return '' . $booking->id . ''; }) ->addColumn('action_lead_edit', function (Booking $booking) { - return ''; + return ''; }) ->addColumn('lead_id', function (Booking $booking) { - return '' . $booking->lead_id . ''; + return '' . $booking->inquiry_id . ''; }) ->addColumn('travel_country_id', function (Booking $booking) { return '' . ($booking->travel_country_id ? $booking->travel_country->name : "-") . ''; @@ -523,7 +523,7 @@ class RequestController extends Controller } }) */ - ->orderColumn('lead_id', 'lead_id $1') + ->orderColumn('lead_id', 'inquiry_id $1') ->orderColumn('id', 'id $1') ->orderColumn('travel_country_id', 'travel_country_id $1') ->orderColumn('travelagenda_id', 'travelagenda_id $1') diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 511c00a..ed04b00 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -14,7 +14,7 @@ class Kernel extends HttpKernel * @var array */ protected $middleware = [ - \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, + \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, @@ -39,19 +39,17 @@ class Kernel extends HttpKernel ], 'api' => [ - 'throttle:60,1', - 'bindings', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + 'throttle:api', ], ]; /** - * The application's route middleware. + * The application's route middleware aliases. * - * These middleware may be assigned to groups or used individually. - * - * @var array + * @var array */ - protected $routeMiddleware = [ + protected $middlewareAliases = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, '2fa' => \App\Http\Middleware\MiddleGoogle2FA::class, diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 7daf51f..c05530f 100755 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -2,8 +2,8 @@ namespace App\Http\Middleware; +use Illuminate\Http\Middleware\TrustProxies as Middleware; use Illuminate\Http\Request; -use Fideloper\Proxy\TrustProxies as Middleware; class TrustProxies extends Middleware { @@ -19,5 +19,11 @@ class TrustProxies extends Middleware * * @var int */ - protected $headers = Request::HEADER_X_FORWARDED_ALL; + protected $headers = + Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_PREFIX | + Request::HEADER_X_FORWARDED_AWS_ELB; } diff --git a/app/Libraries/CreatePDF.php b/app/Libraries/CreatePDF.php index 080b53b..8de7375 100644 --- a/app/Libraries/CreatePDF.php +++ b/app/Libraries/CreatePDF.php @@ -17,7 +17,7 @@ class CreatePDF{ { $this->view = $view; $this->disk = $disk; - $this->prepath = Storage::disk($disk)->getAdapter()->getPathPrefix(); + $this->prepath = Storage::disk($disk)->path(''); } diff --git a/app/Models/Account.php b/app/Models/Account.php index 5b6547d..3cd9e4d 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -32,7 +32,10 @@ class Account extends Model use SoftDeletes; - protected $dates = ['deleted_at']; + protected $casts = [ + 'deleted_at' => 'datetime', + ]; + public function user() diff --git a/app/Models/Arrangement.php b/app/Models/Arrangement.php index 1d4a513..b2111d0 100644 --- a/app/Models/Arrangement.php +++ b/app/Models/Arrangement.php @@ -55,14 +55,12 @@ class Arrangement extends Model 'view_position' => 'int', 'booking_id' => 'int', 'type_id' => 'int', - 'in_pdf' => 'bool' + 'in_pdf' => 'bool', + 'state' => 'datetime', + 'begin' => 'datetime', + 'end' => 'datetime', ]; - protected $dates = [ - 'state', - 'begin', - 'end' - ]; protected $fillable = [ 'template_id', diff --git a/app/Models/Booking.php b/app/Models/Booking.php index 79a91ba..f3bf11c 100644 --- a/app/Models/Booking.php +++ b/app/Models/Booking.php @@ -9,6 +9,7 @@ namespace App\Models; use Carbon\Carbon; use App\Services\Util; use App\Services\Passolution; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Collection; @@ -18,7 +19,7 @@ use Illuminate\Database\Eloquent\Collection; * @property int $id * @property Carbon $booking_date * @property int $customer_id - * @property int $lead_id + * @property int $inquiry_id * @property bool $new_drafts * @property int $sf_guard_user_id * @property int $branch_id @@ -203,13 +204,16 @@ use Illuminate\Database\Eloquent\Collection; */ class Booking extends Model { + use HasFactory; + protected $connection = 'mysql'; protected $table = 'booking'; protected $casts = [ 'customer_id' => 'int', - 'lead_id' => 'int', + 'inquiry_id' => 'int', + 'offer_id' => 'int', 'new_drafts' => 'bool', 'sf_guard_user_id' => 'int', 'branch_id' => 'int', @@ -237,25 +241,24 @@ class Booking extends Model 'is_rail_fly' => 'bool', 'comfort' => 'bool', 'airline_ids' => 'array', - 'participant_pass' => 'bool' - ]; + 'participant_pass' => 'bool', + 'booking_date' => 'datetime', + 'start_date' => 'datetime', + 'end_date' => 'datetime', + 'participant_birthdate' => 'datetime', + 'final_payment_date' => 'datetime', + 'refund_date' => 'datetime', + 'lawyer_date' => 'datetime', + 'xx_tkt_date' => 'datetime', + ]; - protected $dates = [ - 'booking_date', - 'start_date', - 'end_date', - 'participant_birthdate', - 'final_payment_date', - 'refund_date', - 'lawyer_date', - 'xx_tkt_date' - - ]; + protected $fillable = [ 'booking_date', 'customer_id', - 'lead_id', + 'inquiry_id', + 'offer_id', 'new_drafts', 'sf_guard_user_id', 'branch_id', @@ -392,9 +395,29 @@ class Booking extends Model return $this->belongsTo(Customer::class); } + /** + * Lead/Inquiry der Buchung. + * FK-Spalte `inquiry_id` (vormals `lead_id` — Modul 3 Phase 2 Rename). + * Methodenname bleibt `lead()` für Legacy-Kompatibilität; {@see self::inquiry()} + * ist der fachlich korrekte Alias und sollte in neuem Code verwendet werden. + */ public function lead() { - return $this->belongsTo(Lead::class); + return $this->belongsTo(Lead::class, 'inquiry_id'); + } + + public function inquiry() + { + return $this->belongsTo(Lead::class, 'inquiry_id'); + } + + /** + * Angebot, aus dem diese Buchung entstanden ist (Modul 6, Ticket B8). + * Nullable — nicht jede Buchung hat einen Angebots-Vorlauf. + */ + public function offer() + { + return $this->belongsTo(\App\Models\Offer::class); } public function sf_guard_user() diff --git a/app/Models/BookingConfirmation.php b/app/Models/BookingConfirmation.php index fccce72..bb5e69f 100644 --- a/app/Models/BookingConfirmation.php +++ b/app/Models/BookingConfirmation.php @@ -48,12 +48,10 @@ class BookingConfirmation extends Model 'total' => 'float', 'deposit' => 'float', 'final_payment' => 'float', + 'deposit_payment_date' => 'datetime', + 'final_payment_date' => 'datetime', ]; - protected $dates = [ - 'deposit_payment_date', - 'final_payment_date' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/BookingDocument.php b/app/Models/BookingDocument.php index be4612f..2f9f830 100644 --- a/app/Models/BookingDocument.php +++ b/app/Models/BookingDocument.php @@ -71,11 +71,9 @@ class BookingDocument extends Model 'status' => 'int', 'booking_storno_id' => 'int', 'coupon_id' => 'int', + 'date' => 'datetime', ]; - protected $dates = [ - 'date' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/BookingInvoice.php b/app/Models/BookingInvoice.php index 90b33f6..851f4e4 100644 --- a/app/Models/BookingInvoice.php +++ b/app/Models/BookingInvoice.php @@ -48,12 +48,10 @@ class BookingInvoice extends Model 'total' => 'float', 'deposit' => 'float', 'final_payment' => 'float', + 'deposit_payment_date' => 'datetime', + 'final_payment_date' => 'datetime', ]; - protected $dates = [ - 'deposit_payment_date', - 'final_payment_date' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/BookingNotice.php b/app/Models/BookingNotice.php index 448e9e1..a889441 100644 --- a/app/Models/BookingNotice.php +++ b/app/Models/BookingNotice.php @@ -52,12 +52,10 @@ class BookingNotice extends Model 'from_user_id' => 'int', 'to_user_id' => 'int', 'show' => 'bool', - 'important' => 'bool' + 'important' => 'bool', + 'edit_at' => 'datetime', ]; - protected $dates = [ - 'edit_at', - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/BookingServiceItem.php b/app/Models/BookingServiceItem.php index 5972329..41b7937 100644 --- a/app/Models/BookingServiceItem.php +++ b/app/Models/BookingServiceItem.php @@ -55,12 +55,10 @@ class BookingServiceItem extends Model 'service_price' => 'float', 'service_price_refund' => 'float', 'commission' => 'float', - 'is_commission_locked' => 'bool' + 'is_commission_locked' => 'bool', + 'travel_date' => 'datetime', ]; - protected $dates = [ - 'travel_date' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/BookingStorno.php b/app/Models/BookingStorno.php index ecd62b1..b2f6da3 100644 --- a/app/Models/BookingStorno.php +++ b/app/Models/BookingStorno.php @@ -49,13 +49,11 @@ class BookingStorno extends Model 'booking_id' => 'int', 'total' => 'float', 'storno' => 'float', - 'done' => 'bool' + 'done' => 'bool', + 'storno_date' => 'datetime', + 'storno_print' => 'datetime', ]; - protected $dates = [ - 'storno_date', - 'storno_print' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/Contact.php b/app/Models/Contact.php new file mode 100644 index 0000000..2cd1868 --- /dev/null +++ b/app/Models/Contact.php @@ -0,0 +1,164 @@ + 'int', + 'credit_card_type_id' => 'int', + 'country_id' => 'int', + 'merged_into_id' => 'int', + 'birthdate' => 'datetime', + 'credit_card_expiration_date' => 'datetime', + 'merged_at' => 'datetime', + ]; + + protected $fillable = [ + 'salutation_id', + 'title', + 'name', + 'firstname', + 'birthdate', + 'company', + 'street', + 'zip', + 'city', + 'email', + 'phone', + 'phonebusiness', + 'phonemobile', + 'fax', + 'bank', + 'bank_code', + 'bank_account_number', + 'credit_card_type_id', + 'credit_card_number', + 'credit_card_expiration_date', + 'participants_remarks', + 'miscellaneous_remarks', + 'country_id', + 'merged_into_id', + 'merged_at', + ]; + + /** + * Globaler Scope: zusammengeführte Duplikate werden standardmäßig ausgeblendet. + * Für Zugriff auf alle inkl. Duplikate: Contact::withoutGlobalScope('not_merged') + */ + protected static function booted(): void + { + static::addGlobalScope('not_merged', function (Builder $query) { + $query->whereNull('merged_into_id'); + }); + } + + // ── Beziehungen ────────────────────────────────────────────────────────── + + public function mergedInto(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(Contact::class, 'merged_into_id') + ->withoutGlobalScope('not_merged'); + } + + public function mergedContacts(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(Contact::class, 'merged_into_id') + ->withoutGlobalScope('not_merged'); + } + + public function leads(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(Lead::class, 'customer_id')->orderByDesc('created_at'); + } + + public function bookings(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(Booking::class, 'customer_id')->orderByDesc('created_at'); + } + + public function salutation(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(Salutation::class); + } + + public function travel_country(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(TravelCountry::class, 'country_id'); + } + + // ── Hilfsmethoden ──────────────────────────────────────────────────────── + + public function fullName(): string + { + if ($this->firstname) { + return $this->firstname . ' ' . $this->name; + } + return (string) $this->name; + } + + public function isMerged(): bool + { + return $this->merged_into_id !== null; + } + + public static function getCountriesArray(): \Illuminate\Support\Collection + { + return TravelCountry::where('is_customer_country', 1)->get()->pluck('name', 'id'); + } + + public static $salutationType = [ + 1 => 'Herr', + 2 => 'Frau', + 3 => 'Divers/keine Anrede', + 4 => 'Firma', + ]; +} diff --git a/app/Models/Coupon.php b/app/Models/Coupon.php index 5e14a13..9e72937 100644 --- a/app/Models/Coupon.php +++ b/app/Models/Coupon.php @@ -59,14 +59,12 @@ class Coupon extends Model 'customer_id' => 'int', 'booking_id' => 'int', 'value' => 'float', - 'is_redeemed' => 'bool' + 'is_redeemed' => 'bool', + 'issue_date' => 'datetime', + 'valid_date' => 'datetime', + 'redeem_date' => 'datetime', ]; - protected $dates = [ - 'issue_date', - 'valid_date', - 'redeem_date' - ]; protected $fillable = [ 'number', diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 94e618e..9853334 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -9,6 +9,7 @@ namespace App\Models; use App\Models\Sym\TravelCountry; use Carbon\Carbon; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; /** @@ -83,20 +84,26 @@ use Illuminate\Database\Eloquent\Model; */ class Customer extends Model { + use HasFactory; + protected $connection = 'mysql'; - protected $table = 'customer'; + /** + * Modul 3 Phase 2: customer → contacts (RENAME TABLE). + * Der Model-Name bleibt aus Kompatibilität zum Legacy-Code bestehen; + * für die Neuimplementierung steht {@see Contact} bereit. + */ + protected $table = 'contacts'; protected $casts = [ 'salutation_id' => 'int', 'credit_card_type_id' => 'int', - 'country_id' => 'int' - ]; + 'country_id' => 'int', + 'birthdate' => 'datetime', + 'credit_card_expiration_date' => 'datetime', + ]; - protected $dates = [ - 'birthdate', - 'credit_card_expiration_date' - ]; + protected $fillable = [ 'salutation_id', diff --git a/app/Models/CustomerFewoFile.php b/app/Models/CustomerFewoFile.php index 7f00ef3..4e480f0 100644 --- a/app/Models/CustomerFewoFile.php +++ b/app/Models/CustomerFewoFile.php @@ -60,10 +60,11 @@ class CustomerFewoFile extends Model protected $casts = [ 'travel_user_id' => 'int', 'customer_fewo_mail_id' => 'int', - 'size' => 'int' + 'size' => 'int', + 'deleted_at' => 'datetime', ]; - protected $dates = ['deleted_at']; + protected $fillable = [ 'travel_user_id', diff --git a/app/Models/CustomerFewoMail.php b/app/Models/CustomerFewoMail.php index e542dfd..aab22a7 100644 --- a/app/Models/CustomerFewoMail.php +++ b/app/Models/CustomerFewoMail.php @@ -106,15 +106,13 @@ class CustomerFewoMail extends Model 'recipient' => 'array', 'forward' => 'array', 'cc' => 'array', - 'bcc' => 'array' + 'bcc' => 'array', + 'sent_at' => 'datetime', + 'scheduled_at' => 'datetime', + 'delivered_at' => 'datetime', + 'deleted_at' => 'datetime', ]; - protected $dates = [ - 'sent_at', - 'scheduled_at', - 'delivered_at', - 'deleted_at' - ]; protected $fillable = [ 'travel_user_booking_fewo_id', diff --git a/app/Models/CustomerMail.php b/app/Models/CustomerMail.php index 83224e1..e412ea5 100644 --- a/app/Models/CustomerMail.php +++ b/app/Models/CustomerMail.php @@ -109,14 +109,12 @@ class CustomerMail extends Model 'recipient' => 'array', 'forward' => 'array', 'cc' => 'array', - 'bcc' => 'array' + 'bcc' => 'array', + 'sent_at' => 'datetime', + 'scheduled_at' => 'datetime', + 'delivered_at' => 'datetime', ]; - protected $dates = [ - 'sent_at', - 'scheduled_at', - 'delivered_at' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/FewoReservation.php b/app/Models/FewoReservation.php index d1a2fb3..e194938 100644 --- a/app/Models/FewoReservation.php +++ b/app/Models/FewoReservation.php @@ -41,13 +41,11 @@ class FewoReservation extends Model protected $casts = [ 'lodging_id' => 'int', 'status' => 'int', - 'type' => 'int' + 'type' => 'int', + 'from_date' => 'datetime', + 'to_date' => 'datetime', ]; - protected $dates = [ - 'from_date', - 'to_date' - ]; protected $fillable = [ 'lodging_id', diff --git a/app/Models/FewoSeason.php b/app/Models/FewoSeason.php index 5196ff0..f7b721c 100644 --- a/app/Models/FewoSeason.php +++ b/app/Models/FewoSeason.php @@ -43,13 +43,11 @@ class FewoSeason extends Model protected $casts = [ 'minimum_stay' => 'int', - 'only_weekday' => 'int' + 'only_weekday' => 'int', + 'from_date' => 'datetime', + 'to_date' => 'datetime', ]; - protected $dates = [ - 'from_date', - 'to_date' - ]; protected $fillable = [ 'name', diff --git a/app/Models/IQContentFileTag.php b/app/Models/IQContentFileTag.php index 8794e1e..be510d0 100644 --- a/app/Models/IQContentFileTag.php +++ b/app/Models/IQContentFileTag.php @@ -17,7 +17,7 @@ use Illuminate\Database\Eloquent\Model; * @property int $tag_id * @property Carbon $created_at * @property Carbon $updated_at - * + * * //* @property IQContentFile $i_q_content_file * @property IQContentTag $i_q_content_tag * @package App\Models diff --git a/app/Models/IQContentTree.php b/app/Models/IQContentTree.php index 4c3d4a8..46fca9d 100644 --- a/app/Models/IQContentTree.php +++ b/app/Models/IQContentTree.php @@ -57,7 +57,7 @@ class IQContentTree extends Model protected $connection = 'mysql_stern'; - protected $dates = ['deleted_at']; + protected $table = 'i_q_content_trees'; @@ -65,7 +65,9 @@ class IQContentTree extends Model 'name', 'identifier', 'description', 'settings', 'pos', 'active', ]; - protected $casts = ['settings' => 'array']; + protected $casts = ['settings' => 'array', + 'deleted_at' => 'datetime', + ]; public function sluggable(): array { diff --git a/app/Models/IQContentTreeNode.php b/app/Models/IQContentTreeNode.php index 33da61a..a4062d0 100644 --- a/app/Models/IQContentTreeNode.php +++ b/app/Models/IQContentTreeNode.php @@ -72,7 +72,7 @@ class IQContentTreeNode extends Model protected $connection = 'mysql_stern'; - protected $dates = ['deleted_at']; + protected $table = 'i_q_content_tree_nodes'; @@ -80,7 +80,9 @@ class IQContentTreeNode extends Model 'tree_id', 'parent_id', 'lvl', 'name', 'identifier', 'title', 'description', 'settings', 'image', 'pos', 'active', ]; - protected $casts = ['settings' => 'array', 'image' => 'array']; + protected $casts = ['settings' => 'array', 'image' => 'array', + 'deleted_at' => 'datetime', + ]; public function sluggable(): array { diff --git a/app/Models/Inquiry.php b/app/Models/Inquiry.php index c714118..ba86d77 100644 --- a/app/Models/Inquiry.php +++ b/app/Models/Inquiry.php @@ -53,13 +53,11 @@ class Inquiry extends Model 'template_id' => 'int', 'in_pdf' => 'bool', 'type_id' => 'int', - 'view_position' => 'int' + 'view_position' => 'int', + 'begin' => 'datetime', + 'end' => 'datetime', ]; - protected $dates = [ - 'begin', - 'end' - ]; protected $fillable = [ 'lead_id', diff --git a/app/Models/Lead.php b/app/Models/Lead.php index 8b84b9e..f9441f5 100644 --- a/app/Models/Lead.php +++ b/app/Models/Lead.php @@ -9,6 +9,7 @@ namespace App\Models; use Carbon\Carbon; use App\Services\Passolution; use App\Models\Lead as ModelsLead; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Collection; @@ -107,9 +108,16 @@ use Illuminate\Database\Eloquent\Collection; */ class Lead extends Model { + use HasFactory; + protected $connection = 'mysql'; - protected $table = 'lead'; + /** + * Modul 3 Phase 2: lead → inquiries (RENAME TABLE). + * Model-Name bleibt (um Breaking Changes in der gesamten Codebase zu vermeiden); + * fachlich ist das Modell jetzt eine "Inquiry" (Anfrage). + */ + protected $table = 'inquiries'; protected $casts = [ 'customer_id' => 'int', @@ -126,16 +134,14 @@ class Lead extends Model 'travelcategory_id' => 'int', 'price' => 'float', 'pax' => 'int', - 'participant_salutation_id' => 'int' + 'participant_salutation_id' => 'int', + 'request_date' => 'datetime', + 'travelperiod_start' => 'datetime', + 'travelperiod_end' => 'datetime', + 'next_due_date' => 'datetime', + 'participant_birthdate' => 'datetime', ]; - protected $dates = [ - 'request_date', - 'travelperiod_start', - 'travelperiod_end', - 'next_due_date', - 'participant_birthdate' - ]; protected $fillable = [ 'customer_id', diff --git a/app/Models/LeadMail.php b/app/Models/LeadMail.php index ac40ed8..ba15506 100644 --- a/app/Models/LeadMail.php +++ b/app/Models/LeadMail.php @@ -91,14 +91,12 @@ class LeadMail extends Model 'recipient' => 'array', 'forward' => 'array', 'cc' => 'array', - 'bcc' => 'array' + 'bcc' => 'array', + 'sent_at' => 'datetime', + 'scheduled_at' => 'datetime', + 'delivered_at' => 'datetime', ]; - protected $dates = [ - 'sent_at', - 'scheduled_at', - 'delivered_at' - ]; protected $fillable = [ 'lead_id', diff --git a/app/Models/LeadNotice.php b/app/Models/LeadNotice.php index 3ecdad3..28c3420 100644 --- a/app/Models/LeadNotice.php +++ b/app/Models/LeadNotice.php @@ -53,12 +53,10 @@ class LeadNotice extends Model 'from_user_id' => 'int', 'to_user_id' => 'int', 'show' => 'bool', - 'important' => 'bool' + 'important' => 'bool', + 'edit_at' => 'datetime', ]; - protected $dates = [ - 'edit_at' - ]; protected $fillable = [ 'lead_id', diff --git a/app/Models/LeadParticipant.php b/app/Models/LeadParticipant.php index 18d0e5c..3f31a98 100644 --- a/app/Models/LeadParticipant.php +++ b/app/Models/LeadParticipant.php @@ -46,12 +46,10 @@ class LeadParticipant extends Model protected $casts = [ 'lead_id' => 'int', - 'participant_salutation_id' => 'int' + 'participant_salutation_id' => 'int', + 'participant_birthdate' => 'datetime', ]; - protected $dates = [ - 'participant_birthdate' - ]; protected $fillable = [ 'lead_id', diff --git a/app/Models/NewsletterContact.php b/app/Models/NewsletterContact.php index bafd063..11ff219 100644 --- a/app/Models/NewsletterContact.php +++ b/app/Models/NewsletterContact.php @@ -36,6 +36,49 @@ use Illuminate\Database\Eloquent\Collection; * @property-read TravelUser|null $travel_user * @property-read Collection|NewsletterLog[] $logs * @package App\Models + * @property-read mixed $full_name + * @property-read mixed $groups + * @property-read mixed $groups_string + * @property-read mixed $source_label + * @property-read mixed $status_badge + * @property-read mixed $status_color + * @property-read mixed $status_label + * @property-read mixed $total_bookings + * @property-read int|null $logs_count + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact active() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact ferienwohnungen() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact kulturreisen() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact multipleBookers() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact onlyTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact query() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereCustomerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereFirstname($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereGroupFerienwohnungen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereGroupKulturreisen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastBookingAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastSyncedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastTravelEndDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereLastname($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereNotes($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereSource($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereSubscribedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereSyncHash($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereTotalBookingsFerienwohnungen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereTotalBookingsKulturreisen($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereTravelUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereUnsubscribedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact withBookings() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact withTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterContact withoutTrashed() + * @mixin \Eloquent */ class NewsletterContact extends Model { diff --git a/app/Models/NewsletterLog.php b/app/Models/NewsletterLog.php index ec5b2b8..36332a5 100644 --- a/app/Models/NewsletterLog.php +++ b/app/Models/NewsletterLog.php @@ -19,6 +19,19 @@ use Illuminate\Database\Eloquent\Model; * @property-read NewsletterContact $newsletter_contact * @property-read SfGuardUser|null $user * @package App\Models + * @property-read mixed $action_label + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog query() + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereAction($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereMetadata($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereNewsletterContactId($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|NewsletterLog whereUserId($value) + * @mixin \Eloquent */ class NewsletterLog extends Model { diff --git a/app/Models/Offer.php b/app/Models/Offer.php index dd276b9..02b503c 100644 --- a/app/Models/Offer.php +++ b/app/Models/Offer.php @@ -1,56 +1,132 @@ 'int', - 'total' => 'float', - 'binary_data' => 'boolean' - ]; + public const STATUSES = [ + self::STATUS_DRAFT, + self::STATUS_SENT, + self::STATUS_ACCEPTED, + self::STATUS_DECLINED, + self::STATUS_EXPIRED, + self::STATUS_WITHDRAWN, + ]; - protected $fillable = [ - 'lead_id', - 'total', - 'binary_data' - ]; + protected $table = 'offers'; - public function lead() - { - return $this->belongsTo(Lead::class); - } + protected $fillable = [ + 'offer_number', + 'contact_id', + 'inquiry_id', + 'booking_id', + 'status', + 'current_version_id', + 'created_by', + ]; + + protected $casts = [ + 'contact_id' => 'int', + 'inquiry_id' => 'int', + 'booking_id' => 'int', + 'current_version_id' => 'int', + 'created_by' => 'int', + ]; + + public function contact(): BelongsTo + { + return $this->belongsTo(Contact::class); + } + + public function inquiry(): BelongsTo + { + // Nach Modul 3 Phase 2: `Lead`-Model bildet die `inquiries`-Tabelle ab + return $this->belongsTo(Lead::class, 'inquiry_id'); + } + + public function booking(): BelongsTo + { + return $this->belongsTo(Booking::class); + } + + public function currentVersion(): BelongsTo + { + return $this->belongsTo(OfferVersion::class, 'current_version_id'); + } + + public function versions(): HasMany + { + return $this->hasMany(OfferVersion::class)->orderBy('version_no'); + } + + public function accessTokens(): HasMany + { + return $this->hasMany(OfferAccessToken::class); + } + + public function creator(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by'); + } + + public function scopeStatus(Builder $q, string $status): Builder + { + return $q->where('status', $status); + } + + public function scopeOpen(Builder $q): Builder + { + return $q->whereIn('status', [self::STATUS_DRAFT, self::STATUS_SENT]); + } + + public function isEditable(): bool + { + return in_array($this->status, [self::STATUS_DRAFT, self::STATUS_SENT], true); + } } diff --git a/app/Models/OfferAccessToken.php b/app/Models/OfferAccessToken.php new file mode 100644 index 0000000..54fb1c3 --- /dev/null +++ b/app/Models/OfferAccessToken.php @@ -0,0 +1,120 @@ + 'int', + 'offer_version_id' => 'int', + 'expires_at' => 'datetime', + 'first_opened_at' => 'datetime', + 'revoked_at' => 'datetime', + ]; + + public function offer(): BelongsTo + { + return $this->belongsTo(Offer::class); + } + + public function version(): BelongsTo + { + return $this->belongsTo(OfferVersion::class, 'offer_version_id'); + } + + public function scopeActive(Builder $q): Builder + { + return $q->whereNull('revoked_at') + ->where(function (Builder $q) { + $q->whereNull('expires_at')->orWhere('expires_at', '>', now()); + }); + } + + /** + * Erzeugt ein neues Token für die angegebene Version und liefert + * den Klartext-Token zurück (nur einmalig abrufbar). In der + * Datenbank wird nur der Hash persistiert. + */ + public static function generate( + Offer $offer, + OfferVersion $version, + ?Carbon $expiresAt = null + ): array { + $plain = Str::random(48); + $hash = hash('sha256', $plain); + + /** @var self $token */ + $token = self::create([ + 'offer_id' => $offer->id, + 'offer_version_id' => $version->id, + 'token_hash' => $hash, + 'expires_at' => $expiresAt, + ]); + + return ['plain' => $plain, 'token' => $token]; + } + + /** + * Lookup per Klartext-Token (konstantzeitig via DB-Unique-Index). + */ + public static function findByPlainToken(string $plain): ?self + { + return self::where('token_hash', hash('sha256', $plain))->first(); + } + + public function isActive(): bool + { + if ($this->revoked_at !== null) { + return false; + } + if ($this->expires_at !== null && $this->expires_at->isPast()) { + return false; + } + + return true; + } +} diff --git a/app/Models/OfferFile.php b/app/Models/OfferFile.php new file mode 100644 index 0000000..be9332c --- /dev/null +++ b/app/Models/OfferFile.php @@ -0,0 +1,99 @@ + 'int', + 'size' => 'int', + 'include_in_pdf' => 'bool', + ]; + + public static array $iconExt = [ + 'default' => 'fa fa-file', + 'pdf' => 'fa fa-file-pdf', + 'jpg' => 'fa fa-file-image', + 'jpeg' => 'fa fa-file-image', + 'png' => 'fa fa-file-image', + 'doc' => 'fa fa-file-word', + 'docx' => 'fa fa-file-word', + ]; + + public function version(): BelongsTo + { + return $this->belongsTo(OfferVersion::class, 'offer_version_id'); + } + + public function getIconExt(): string + { + return self::$iconExt[$this->ext] ?? self::$iconExt['default']; + } + + public function getURL(bool|string $do = false): string + { + return route('storage_file', [$this->id, 'offer', $do]); + } + + public function getPath(): string + { + return \Storage::disk('offer')->path($this->dir . $this->filename); + } + + public function formatBytes(int $precision = 2): string + { + $size = $this->size; + if ($size <= 0) { + return (string) $size; + } + + $base = log($size) / log(1024); + $suffixes = [' bytes', ' KB', ' MB', ' GB', ' TB']; + + return round(1024 ** ($base - floor($base)), $precision) . $suffixes[floor($base)]; + } +} diff --git a/app/Models/OfferItem.php b/app/Models/OfferItem.php new file mode 100644 index 0000000..9baf8f8 --- /dev/null +++ b/app/Models/OfferItem.php @@ -0,0 +1,86 @@ + 'int', + 'position' => 'int', + 'quantity' => 'int', + 'price_per_unit' => 'decimal:2', + 'total_price' => 'decimal:2', + 'travel_program_id' => 'int', + 'fewo_lodging_id' => 'int', + 'metadata' => 'array', + ]; + + public function version(): BelongsTo + { + return $this->belongsTo(OfferVersion::class, 'offer_version_id'); + } + + /** + * Aus Menge × Einzelpreis den Positions-Gesamtpreis berechnen + * (Rabatte negativ — gehört in den Service-Layer zur Summierung). + */ + public function calculateTotal(): float + { + return round($this->quantity * (float) $this->price_per_unit, 2); + } +} diff --git a/app/Models/OfferTemplate.php b/app/Models/OfferTemplate.php new file mode 100644 index 0000000..2f49ec6 --- /dev/null +++ b/app/Models/OfferTemplate.php @@ -0,0 +1,77 @@ + 'int', + 'default_items' => 'array', + 'is_active' => 'bool', + 'created_by' => 'int', + ]; + + public function branch(): BelongsTo + { + return $this->belongsTo(Branch::class); + } + + public function creator(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by'); + } + + public function scopeActive(Builder $q): Builder + { + return $q->where('is_active', true); + } +} diff --git a/app/Models/OfferVersion.php b/app/Models/OfferVersion.php new file mode 100644 index 0000000..d85b1a5 --- /dev/null +++ b/app/Models/OfferVersion.php @@ -0,0 +1,126 @@ + 'int', + 'version_no' => 'int', + 'valid_until' => 'date', + 'total_price' => 'decimal:2', + 'template_id' => 'int', + 'pdf_archived' => 'bool', + 'sent_at' => 'datetime', + 'accepted_at' => 'datetime', + 'template_document_ids' => 'array', + 'created_by' => 'int', + ]; + + public function offer(): BelongsTo + { + return $this->belongsTo(Offer::class); + } + + public function template(): BelongsTo + { + return $this->belongsTo(OfferTemplate::class, 'template_id'); + } + + public function items(): HasMany + { + return $this->hasMany(OfferItem::class)->orderBy('position'); + } + + public function files(): HasMany + { + return $this->hasMany(OfferFile::class); + } + + public function creator(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by'); + } + + public function isEditable(): bool + { + return $this->status === self::STATUS_DRAFT; + } +} diff --git a/app/Models/Page.php b/app/Models/Page.php index 702c5f6..82e8ac4 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -152,12 +152,10 @@ class Page extends Model 'tree_root' => 'int', 'parent_id' => 'int', 'travel_guide_content_id' => 'int', - 'fewo_lodging' => 'int' + 'fewo_lodging' => 'int', + 'date' => 'datetime', ]; - protected $dates = [ - 'date' - ]; protected $fillable = [ 'owner', diff --git a/app/Models/Participant.php b/app/Models/Participant.php index 12a3dc6..7d84851 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -53,13 +53,10 @@ class Participant extends Model 'participant_salutation_id' => 'int', 'participant_child' => 'bool', 'participant_pass' => 'bool', - 'participant_storno' => 'bool' - + 'participant_storno' => 'bool', + 'participant_birthdate' => 'datetime', ]; - protected $dates = [ - 'participant_birthdate' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/ServiceProviderEntry.php b/app/Models/ServiceProviderEntry.php index 07ee5a2..055f7ba 100644 --- a/app/Models/ServiceProviderEntry.php +++ b/app/Models/ServiceProviderEntry.php @@ -55,12 +55,10 @@ class ServiceProviderEntry extends Model 'amount' => 'float', 'amount_eur' => 'float', 'factor' => 'float', - 'is_cleared' => 'bool' + 'is_cleared' => 'bool', + 'payment_date' => 'datetime', ]; - protected $dates = [ - 'payment_date' - ]; protected $fillable = [ 'booking_id', diff --git a/app/Models/StatusHistory.php b/app/Models/StatusHistory.php index 16b3fae..4a5b647 100644 --- a/app/Models/StatusHistory.php +++ b/app/Models/StatusHistory.php @@ -47,14 +47,12 @@ class StatusHistory extends Model protected $casts = [ 'status_id' => 'int', 'lead_id' => 'int', - 'sf_guard_user_id' => 'int' + 'sf_guard_user_id' => 'int', + 'date' => 'datetime', + 'target_date' => 'datetime', + 'created_at' => 'datetime', ]; - protected $dates = [ - 'date', - 'target_date', - 'created_at' - ]; protected $fillable = [ 'status_id', diff --git a/app/Models/TravelBooking.php b/app/Models/TravelBooking.php index b8f5b2c..357e380 100644 --- a/app/Models/TravelBooking.php +++ b/app/Models/TravelBooking.php @@ -137,15 +137,13 @@ class TravelBooking extends Model 'options' => 'array', 'class_options' => 'array', 'extra_category' => 'array', - 'insurances' => 'array' + 'insurances' => 'array', + 'created' => 'datetime', + 'selected_start_date' => 'datetime', + 'selected_end_date' => 'datetime', + 'final_payment_date' => 'datetime', ]; - protected $dates = [ - 'created', - 'selected_start_date', - 'selected_end_date', - 'final_payment_date' - ]; protected $fillable = [ 'crm_booking_id', diff --git a/app/Models/TravelUser.php b/app/Models/TravelUser.php index 60956d9..86906be 100644 --- a/app/Models/TravelUser.php +++ b/app/Models/TravelUser.php @@ -79,12 +79,10 @@ class TravelUser extends Model protected $casts = [ 'salutation_id' => 'int', 'travel_nationality_id' => 'int', - 'last_user_data' => 'array' + 'last_user_data' => 'array', + 'birthday' => 'datetime', ]; - protected $dates = [ - 'birthday' - ]; protected $hidden = [ 'password' diff --git a/app/Models/TravelUserBookingFewo.php b/app/Models/TravelUserBookingFewo.php index 052ea55..b8332b7 100644 --- a/app/Models/TravelUserBookingFewo.php +++ b/app/Models/TravelUserBookingFewo.php @@ -159,14 +159,12 @@ class TravelUserBookingFewo extends Model 'send_service_mail' => 'array', 'send_info_mail' => 'array', 'send_employee_mail' => 'array', + 'booking_date' => 'datetime', + 'from_date' => 'datetime', + 'to_date' => 'datetime', + 'deleted_at' => 'datetime', ]; - protected $dates = [ - 'booking_date', - 'from_date', - 'to_date', - 'deleted_at' - ]; protected $fillable = [ 'travel_user_id', @@ -545,7 +543,7 @@ class TravelUserBookingFewo extends Model if(!Storage::disk('fewo_invoices')->exists( $dir )){ Storage::disk('fewo_invoices')->makeDirectory($dir); //creates directory } - $path = Storage::disk('fewo_invoices')->getAdapter()->getPathPrefix(); + $path = Storage::disk('fewo_invoices')->path(''); return $path.$dir; } @@ -621,7 +619,7 @@ class TravelUserBookingFewo extends Model if(!Storage::disk('fewo_infos')->exists( $dir )){ Storage::disk('fewo_infos')->makeDirectory($dir); //creates directory } - $path = Storage::disk('fewo_infos')->getAdapter()->getPathPrefix(); + $path = Storage::disk('fewo_infos')->path(''); return $path.$dir; } diff --git a/app/Models/TravelUserBookingFewoNotice.php b/app/Models/TravelUserBookingFewoNotice.php index 1d291e6..c9a0a40 100644 --- a/app/Models/TravelUserBookingFewoNotice.php +++ b/app/Models/TravelUserBookingFewoNotice.php @@ -53,12 +53,10 @@ class TravelUserBookingFewoNotice extends Model 'from_user_id' => 'int', 'to_user_id' => 'int', 'show' => 'bool', - 'important' => 'bool' + 'important' => 'bool', + 'edit_at' => 'datetime', ]; - protected $dates = [ - 'edit_at' - ]; protected $fillable = [ 'travel_user_booking_fewo_id', diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 7294c85..bb413be 100755 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,9 +2,8 @@ namespace App\Providers; -use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; -use Laravel\Passport\Passport; +use Illuminate\Support\Facades\Gate; class AuthServiceProvider extends ServiceProvider { @@ -22,12 +21,8 @@ class AuthServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); - Passport::routes(); - - - // } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 5ea48d3..37ce2bf 100755 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,72 +2,47 @@ namespace App\Providers; -use Illuminate\Support\Facades\Route; +use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Facades\Route; class RouteServiceProvider extends ServiceProvider { /** - * This namespace is applied to your controller routes. - * - * In addition, it is set as the URL generator's root namespace. + * The path to your application's "home" route. * * @var string */ + public const HOME = '/home'; + + /** + * The controller namespace for the application. + * Kept for backwards compatibility with string-based route definitions. + * + * @var string|null + */ protected $namespace = 'App\Http\Controllers'; /** - * Define your route model bindings, pattern filters, etc. - * - * @return void + * Define your route model bindings, pattern filters, and other route configuration. */ - public function boot() + public function boot(): void { - // + RateLimiter::for('api', function (Request $request) { + return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); + }); - parent::boot(); - } + $this->routes(function () { + Route::middleware('api') + ->prefix('api') + ->namespace($this->namespace) + ->group(base_path('routes/api.php')); - /** - * Define the routes for the application. - * - * @return void - */ - public function map() - { - $this->mapApiRoutes(); - - $this->mapWebRoutes(); - - // - } - - /** - * Define the "web" routes for the application. - * - * These routes all receive session state, CSRF protection, etc. - * - * @return void - */ - protected function mapWebRoutes() - { - Route::middleware('web') - ->namespace($this->namespace) - ->group(base_path('routes/web.php')); - } - - /** - * Define the "api" routes for the application. - * - * These routes are typically stateless. - * - * @return void - */ - protected function mapApiRoutes() - { - Route::prefix('api') - ->middleware('api') - ->namespace($this->namespace) - ->group(base_path('routes/api.php')); + Route::middleware('web') + ->namespace($this->namespace) + ->group(base_path('routes/web.php')); + }); } } diff --git a/app/Repositories/BookingPDFRepository.php b/app/Repositories/BookingPDFRepository.php index 505ddaf..e5ba139 100644 --- a/app/Repositories/BookingPDFRepository.php +++ b/app/Repositories/BookingPDFRepository.php @@ -22,7 +22,7 @@ class BookingPDFRepository extends BaseRepository public function __construct(Booking $model) { $this->model = $model; - $this->prepath = Storage::disk('public')->getAdapter()->getPathPrefix(); + $this->prepath = Storage::disk('public')->path(''); } public function update($data) @@ -63,13 +63,13 @@ class BookingPDFRepository extends BaseRepository { $document = new stdClass(); $document->name = 'registration'; - $document->number = $this->model->lead_id; + $document->number = $this->model->inquiry_id; $document->title = 'BUCHUNGSAUFTRAG'; $document->voucher = null; $document->date = now(); $document->total = $this->model->getPriceRaw(); $dir = $this->getDirPath('pdf', 'booking', $document->date->format('Y')); - $filename = "Buchnungsauftrag-" . $this->model->lead_id . ".pdf"; + $filename = "Buchnungsauftrag-" . $this->model->inquiry_id . ".pdf"; $pdf_file = new CreatePDF('pdf.booking_registration'); $data = [ 'booking' => $this->model, @@ -85,7 +85,7 @@ class BookingPDFRepository extends BaseRepository { $document = new stdClass(); $document->name = 'confirmation'; - $document->number = $this->model->lead_id; + $document->number = $this->model->inquiry_id; $document->title = 'REISEBESTÄTIGUNG'; $document->voucher = null; $document->date = now(); @@ -104,7 +104,7 @@ class BookingPDFRepository extends BaseRepository $document->final_payment_date = date('Y-m-d'); } $dir = $this->getDirPath('pdf', 'booking', $document->date->format('Y')); - $filename = "Reisebestätigung-" . $this->model->lead_id . ".pdf"; + $filename = "Reisebestätigung-" . $this->model->inquiry_id . ".pdf"; $pdf_file = new CreatePDF('pdf.booking_confirmation'); $data = [ @@ -160,14 +160,14 @@ class BookingPDFRepository extends BaseRepository { $document = new stdClass(); $document->name = 'voucher'; - $document->number = $this->model->lead_id; + $document->number = $this->model->inquiry_id; $document->name = 'voucher'; $document->title = $agency ? 'VOUCHER Agentur' : 'VOUCHER'; $document->voucher = $agency ? 'agency' : 'client'; $document->date = now(); $dir = $this->getDirPath('pdf', 'voucher', $document->date->format('Y')); - $filename = ($agency ? 'VoucherAgentur' : 'Voucher') . "-" . $this->model->lead_id . ".pdf"; + $filename = ($agency ? 'VoucherAgentur' : 'Voucher') . "-" . $this->model->inquiry_id . ".pdf"; $pdf_file = new CreatePDF('pdf.booking_voucher'); $data = [ @@ -224,7 +224,7 @@ class BookingPDFRepository extends BaseRepository //init document $document = new stdClass(); $document->name = $identifier; - $document->number = $this->model->lead_id; + $document->number = $this->model->inquiry_id; $document->title = 'STORNOBESTÄTIGUNG'; $document->voucher = null; $document->date = Carbon::parse($data['storno_print']); @@ -253,7 +253,7 @@ class BookingPDFRepository extends BaseRepository $dir = $this->getDirPath('pdf', 'storno', $document->date->format('Y')); - $filename = "Reisestornierung -" . $this->model->lead_id . ".pdf"; + $filename = "Reisestornierung -" . $this->model->inquiry_id . ".pdf"; $pdf_file = new CreatePDF('pdf.booking_storno'); $data = [ @@ -288,7 +288,9 @@ class BookingPDFRepository extends BaseRepository $fill = [ 'booking_id' => $this->model->id, 'customer_id' => $this->model->customer_id, - 'lead_id' => $this->model->lead_id, + // booking_documents.lead_id ist ein Shadow-Feld von booking.inquiry_id; + // die Spalte selbst wird von Phase 2 nicht umbenannt. + 'lead_id' => $this->model->inquiry_id, 'identifier' => $identifier, 'filename' => $filename, 'dir' => $dir, diff --git a/app/Repositories/ContactRepository.php b/app/Repositories/ContactRepository.php new file mode 100644 index 0000000..f37e669 --- /dev/null +++ b/app/Repositories/ContactRepository.php @@ -0,0 +1,28 @@ +model = $model; + } + + public function updateContact(int|string $id, array $data): Contact + { + /** @var Contact $contact */ + $contact = Contact::findOrFail($id); + $contact->fill($data); + $contact->save(); + + return $contact; + } + + public function createContact(array $data): Contact + { + return Contact::create($data); + } +} diff --git a/app/Repositories/CustomerMailRepository.php b/app/Repositories/CustomerMailRepository.php index c1b1226..6397f0c 100644 --- a/app/Repositories/CustomerMailRepository.php +++ b/app/Repositories/CustomerMailRepository.php @@ -135,7 +135,8 @@ class CustomerMailRepository extends BaseRepository { $customer_mail->fill([ 'booking_id' => $booking->id, 'customer_id' => $booking->customer_id, - 'lead_id' => $booking->lead_id, + // customer_mails.lead_id-Spalte bleibt unverändert; Wert kommt aus booking.inquiry_id + 'lead_id' => $booking->inquiry_id, 'is_answer' => $is_answer, 'reply_id' => $reply_id, 'email' => $mail_from, @@ -153,7 +154,8 @@ class CustomerMailRepository extends BaseRepository { $customer_mail = CustomerMail::create([ 'booking_id' => $booking->id, 'customer_id' => $booking->customer_id, - 'lead_id' => $booking->lead_id, + // customer_mails.lead_id-Spalte bleibt unverändert; Wert kommt aus booking.inquiry_id + 'lead_id' => $booking->inquiry_id, 'is_answer' => $is_answer, 'reply_id' => $reply_id, 'email' => $mail_from, @@ -300,7 +302,7 @@ class CustomerMailRepository extends BaseRepository { $value->id = $customer_mail->booking_id; $value->booking = $booking; $value->show = 'single'; - $value->lead_title_id = " - (".$value->booking->lead_id.")"; + $value->lead_title_id = " - (".$value->booking->inquiry_id.")"; $tmp = []; @@ -342,7 +344,7 @@ class CustomerMailRepository extends BaseRepository { $value->booking = $booking; $value->show = 'single'; $value->draft = true; - $value->lead_title_id = " - (".$value->booking->lead_id.")"; + $value->lead_title_id = " - (".$value->booking->inquiry_id.")"; }else{ //multi @@ -379,8 +381,8 @@ class CustomerMailRepository extends BaseRepository { $value->draft = false; $value->booking = $booking; $value->message = ""; - $value->subject = " - (".$value->booking->lead_id.")"; - $value->lead_title_id = " - (".$value->booking->lead_id.")"; + $value->subject = " - (".$value->booking->inquiry_id.")"; + $value->lead_title_id = " - (".$value->booking->inquiry_id.")"; $value->s_placeholder = "Betreff des Kunden"; $value->m_placeholder = "Nachricht des Kunden"; if(isset($data['customer_mail_id']) && $customer_mail = CustomerMail::find($data['customer_mail_id'])){ diff --git a/app/Repositories/LeadRepository.php b/app/Repositories/LeadRepository.php index 929426d..ad0ac87 100644 --- a/app/Repositories/LeadRepository.php +++ b/app/Repositories/LeadRepository.php @@ -134,7 +134,7 @@ class LeadRepository extends BaseRepository { $data = [ 'booking_date' => date('Y-m-d'), //now 'customer_id' => $this->model->customer->id, - 'lead_id' => $this->model->id, + 'inquiry_id' => $this->model->id, 'new_drafts' => 1, 'sf_guard_user_id' => $this->model->sf_guard_user_id, 'branch_id' => 4, diff --git a/app/Services/Booking.php b/app/Services/Booking.php index 64ed181..f831388 100644 --- a/app/Services/Booking.php +++ b/app/Services/Booking.php @@ -1,127 +1,84 @@ get()->sortByDesc('pos')->pluck('slug', 'id'); - return $booking_email_files; + public static function contentFiles(): \Illuminate\Support\Collection + { + return CMSContent::where('identifier', '=', 'booking-email-file') + ->get() + ->sortByDesc('pos') + ->pluck('slug', 'id'); } - public static function setOutputDirs($dir, $subdir){ - self::$output_dirs[$dir][] = $subdir; + public static function setOutputDirs(string $dir, string $subdir): void + { + MailDirService::setOutputDir($dir, $subdir); } - public static function getMailDirNotInOutput($booking_id, $dir){ - $is_o_dirs = isset(self::$output_dirs[$dir]) ? self::$output_dirs[$dir] : []; - $ret = []; - $CustomerMails = CustomerMail::whereBookingId($booking_id)->whereDir($dir)->get(); - if($CustomerMails){ - foreach($CustomerMails as $CustomerMail){ - if(!in_array($CustomerMail->subdir, $is_o_dirs)){ - $ret[] = $CustomerMail->subdir; - } + /** + * @return string[] + */ + public static function getMailDirNotInOutput(int $bookingId, string $dir): array + { + $mails = CustomerMail::whereBookingId($bookingId)->whereDir($dir)->get(); + return MailDirService::getMailDirsNotInOutput($mails, $dir); + } + + public static function getCustomerMailDirs(): \Illuminate\Database\Eloquent\Collection + { + return MailDirService::getCustomerMailDirs(); + } + + public static function getCustomerMailDir(int $id): ?\App\Models\CMSContent + { + return MailDirService::getCustomerMailDir($id); + } + + public static function getCustomerMailName(\App\Models\CMSContent $mailDir, int $mailDirId): string + { + return MailDirService::getCustomerMailName($mailDir, $mailDirId); + } + + public static function getCustomerMailEmails(\App\Models\CMSContent $mailDir, int $mailDirId): array|string + { + return MailDirService::getCustomerMailEmails($mailDir, $mailDirId); + } + + public static function getBookingInstructionPDFName(\App\Models\FewoLodging $fewo): string + { + return "HINWEISE-FERIENWOHNUNG-" . $fewo->pdf_name . ".pdf"; + } + + public static function getBookingCMSContent(\App\Models\CMSContent $content, string $identifier): \Illuminate\Database\Eloquent\Collection + { + return CMSContent::where('identifier', '=', $identifier) + ->where('integer', $content->id) + ->get() + ->sortBy('pos'); + } + + public static function getBookingCMSContentForPDF(string $identifierContent, string $identifier): array + { + $pdfContent = []; + $contents = CMSContent::where('identifier', '=', $identifierContent)->get()->sortBy('pos'); + + foreach ($contents as $content) { + if ($content->decimal > 0) { + $pdfContent[] = $content; } - } - return $ret; - } - - public static function getCustomerMailDirs(){ - $customer_mail_dirs = CMSContent::where('identifier', '=', 'customer-mail-dirs')->get()->sortBy('pos'); - return $customer_mail_dirs; - } - - public static function getCustomerMailDir($id){ - return CMSContent::where('identifier', '=', 'customer-mail-dirs')->where('pos', '=', $id)->first(); - } - - public static function getCustomerMailName($customer_mail_dir, $mail_dir_id){ - - switch ($customer_mail_dir->getArrayContent('model')){ - case 'TravelCountry': - $model = \App\Models\Sym\TravelCountry::find($mail_dir_id); - break; - case 'Airline': - $model = Airline::find($mail_dir_id); - break; - case 'Insurance': - $model = Insurance::find($mail_dir_id); - break; - case 'TravelCompany': - $model = TravelCompany::find($mail_dir_id); - break; - default: - return ''; - } - - if($model){ - if($customer_mail_dir->getArrayContent('model') === 'TravelCountry'){ - return $model->mail_dir_name; - } - return $model->name; - } - return ""; - } - - public static function getCustomerMailEmails($customer_mail_dir, $mail_dir_id){ - - switch ($customer_mail_dir->getArrayContent('model')){ - case 'TravelCountry': - $model = \App\Models\Sym\TravelCountry::find($mail_dir_id); - break; - case 'Airline': - $model = Airline::find($mail_dir_id); - break; - case 'Insurance': - $model = Insurance::find($mail_dir_id); - break; - case 'TravelCompany': - $model = TravelCompany::find($mail_dir_id); - break; - default: - //direkt from CMSContent - return $customer_mail_dir->getArrayContent('emails'); - } - - if($model){ - return $model->contact_emails; - } - return []; - } - - public static function getBookingInstructionPDFName($fewo){ - return "HINWEISE-FERIENWOHNUNG-".$fewo->pdf_name.".pdf"; - } - - public static function getBookingCMSContent($content, $identifier){ - return CMSContent::where('identifier', '=', $identifier)->where('integer', $content->id)->get()->sortBy('pos'); - } - - public static function getBookingCMSContentForPDF($identifier_content, $identifier){ - $pdf_content = []; - $contents = CMSContent::where('identifier', '=', $identifier_content)->get()->sortBy('pos'); - foreach ($contents as $content){ - if($content->decimal > 0){ //in_pdf - $pdf_content[] = $content; - } - if($fewo_contents = self::getBookingCMSContent($content, $identifier)){ - foreach ($fewo_contents as $fewo_content){ - if($fewo_content->decimal > 0){ //in_pdf - $pdf_content[] = $fewo_content; - } + foreach (self::getBookingCMSContent($content, $identifier) as $fewoContent) { + if ($fewoContent->decimal > 0) { + $pdfContent[] = $fewoContent; } } } - return $pdf_content; + + return $pdfContent; } -} \ No newline at end of file +} diff --git a/app/Services/BookingImport.php b/app/Services/BookingImport.php index acff286..2e6988d 100644 --- a/app/Services/BookingImport.php +++ b/app/Services/BookingImport.php @@ -53,7 +53,7 @@ class BookingImport $data = [ 'booking_date' => $travel_booking->created->format('Y-m-d'), 'customer_id' => $customer->id, - 'lead_id' => $lead->id, + 'inquiry_id' => $lead->id, 'new_drafts' => $travel_booking->drafts === null ? 0 : 1, 'sf_guard_user_id' => 15, 'branch_id' => 4, diff --git a/app/Services/Lead.php b/app/Services/Lead.php index 5868452..e6b47dd 100644 --- a/app/Services/Lead.php +++ b/app/Services/Lead.php @@ -1,124 +1,51 @@ get()->sortByDesc('pos')->pluck('slug', 'id'); - return $lead_files; + public static function contentFiles(): \Illuminate\Support\Collection + { + return CMSContent::where('identifier', '=', 'lead-email-file') + ->get() + ->sortByDesc('pos') + ->pluck('slug', 'id'); } - public static function setOutputDirs($dir, $subdir){ - self::$output_dirs[$dir][] = $subdir; + public static function setOutputDirs(string $dir, string $subdir): void + { + MailDirService::setOutputDir($dir, $subdir); } - public static function getMailDirNotInOutput($lead_id, $dir){ - $is_o_dirs = isset(self::$output_dirs[$dir]) ? self::$output_dirs[$dir] : []; - $ret = []; - $LeadMails = LeadMail::whereLeadId($lead_id)->whereDir($dir)->get(); - if($LeadMails){ - foreach($LeadMails as $LeadMail){ - if(!in_array($LeadMail->subdir, $is_o_dirs)){ - $ret[] = $LeadMail->subdir; - } - } - } - return $ret; - } - - public static function getCustomerMailDirs(){ - $customer_mail_dirs = CMSContent::where('identifier', '=', 'customer-mail-dirs')->get()->sortBy('pos'); - return $customer_mail_dirs; + /** + * @return string[] + */ + public static function getMailDirNotInOutput(int $leadId, string $dir): array + { + $mails = LeadMail::whereLeadId($leadId)->whereDir($dir)->get(); + return MailDirService::getMailDirsNotInOutput($mails, $dir); } - public static function getCustomerMailDir($id){ - return CMSContent::where('identifier', '=', 'customer-mail-dirs')->where('pos', '=', $id)->first(); + public static function getCustomerMailDirs(): \Illuminate\Database\Eloquent\Collection + { + return MailDirService::getCustomerMailDirs(); } - public static function getCustomerMailName($lead_mail_dir, $mail_dir_id){ - - switch ($lead_mail_dir->getArrayContent('model')){ - case 'TravelCountry': - $model = \App\Models\Sym\TravelCountry::find($mail_dir_id); - break; - case 'Airline': - $model = Airline::find($mail_dir_id); - break; - case 'Insurance': - $model = Insurance::find($mail_dir_id); - break; - case 'TravelCompany': - $model = TravelCompany::find($mail_dir_id); - break; - default: - return ''; - } - - if($model){ - if($lead_mail_dir->getArrayContent('model') === 'TravelCountry'){ - return $model->mail_dir_name; - } - return $model->name; - } - return ""; + public static function getCustomerMailDir(int $id): ?\App\Models\CMSContent + { + return MailDirService::getCustomerMailDir($id); } - public static function getCustomerMailEmails($lead_mail_dir, $mail_dir_id){ - - switch ($lead_mail_dir->getArrayContent('model')){ - case 'TravelCountry': - $model = \App\Models\Sym\TravelCountry::find($mail_dir_id); - break; - case 'Airline': - $model = Airline::find($mail_dir_id); - break; - case 'Insurance': - $model = Insurance::find($mail_dir_id); - break; - case 'TravelCompany': - $model = TravelCompany::find($mail_dir_id); - break; - default: - //direkt from CMSContent - return $lead_mail_dir->getArrayContent('emails'); - } - - if($model){ - return $model->contact_emails; - } - return []; + public static function getCustomerMailName(\App\Models\CMSContent $mailDir, int $mailDirId): string + { + return MailDirService::getCustomerMailName($mailDir, $mailDirId); } - /*public static function getFeWoInstructionPDFName($fewo){ - return "HINWEISE-FERIENWOHNUNG-".$fewo->pdf_name.".pdf"; + public static function getCustomerMailEmails(\App\Models\CMSContent $mailDir, int $mailDirId): array|string + { + return MailDirService::getCustomerMailEmails($mailDir, $mailDirId); } - public static function getFeWoCMSContent($content, $identifier_fewo){ - return CMSContent::where('identifier', '=', $identifier_fewo)->where('integer', $content->id)->get()->sortBy('pos'); - } - - public static function getFeWoCMSContentForPDF($identifier_content, $identifier_fewo){ - $pdf_content = []; - $contents = CMSContent::where('identifier', '=', $identifier_content)->get()->sortBy('pos'); - foreach ($contents as $content){ - if($content->decimal > 0){ //in_pdf - $pdf_content[] = $content; - } - if($fewo_contents = BookingFewo::getFeWoCMSContent($content, $identifier_fewo)){ - foreach ($fewo_contents as $fewo_content){ - if($fewo_content->decimal > 0){ //in_pdf - $pdf_content[] = $fewo_content; - } - } - } - } - return $pdf_content; - }*/ -} \ No newline at end of file +} diff --git a/app/Services/MailDirService.php b/app/Services/MailDirService.php new file mode 100644 index 0000000..f36cec6 --- /dev/null +++ b/app/Services/MailDirService.php @@ -0,0 +1,92 @@ +get()->sortBy('pos'); + } + + public static function getCustomerMailDir(int $id): ?CMSContent + { + return CMSContent::where('identifier', '=', 'customer-mail-dirs')->where('pos', '=', $id)->first(); + } + + public static function getCustomerMailName(CMSContent $mailDir, int $mailDirId): string + { + $model = self::resolveModel($mailDir, $mailDirId); + + if ($model === null) { + return ''; + } + + if ($mailDir->getArrayContent('model') === 'TravelCountry') { + return $model->mail_dir_name ?? ''; + } + + return $model->name ?? ''; + } + + /** + * @return array|string + */ + public static function getCustomerMailEmails(CMSContent $mailDir, int $mailDirId): array|string + { + $model = self::resolveModel($mailDir, $mailDirId); + + if ($model === null) { + // Default: emails come directly from CMSContent + return $mailDir->getArrayContent('emails') ?? []; + } + + return $model->contact_emails ?? []; + } + + /** + * Returns subdirs from a mail collection that were not already in the output. + * + * @param \Illuminate\Database\Eloquent\Collection $mails Collection of CustomerMail or LeadMail + * @return string[] + */ + public static function getMailDirsNotInOutput(iterable $mails, string $dir): array + { + $processed = self::$outputDirs[$dir] ?? []; + $result = []; + + foreach ($mails as $mail) { + if (!in_array($mail->subdir, $processed, true)) { + $result[] = $mail->subdir; + } + } + + return $result; + } + + private static function resolveModel(CMSContent $mailDir, int $mailDirId): mixed + { + return match ($mailDir->getArrayContent('model')) { + 'TravelCountry' => \App\Models\Sym\TravelCountry::find($mailDirId), + 'Airline' => Airline::find($mailDirId), + 'Insurance' => Insurance::find($mailDirId), + 'TravelCompany' => TravelCompany::find($mailDirId), + default => null, + }; + } +} diff --git a/app/Services/Passolution.php b/app/Services/Passolution.php index dea1663..12bb01e 100644 --- a/app/Services/Passolution.php +++ b/app/Services/Passolution.php @@ -221,7 +221,7 @@ class Passolution if(!Storage::disk('public')->exists( $this->pdf_dir )){ Storage::disk('public')->makeDirectory($this->pdf_dir); //creates directory } - $this->pdf_path = Storage::disk('public')->getAdapter()->getPathPrefix(); + $this->pdf_path = Storage::disk('public')->path(''); } } diff --git a/app/Services/Util.php b/app/Services/Util.php index 76884ff..d67c00d 100644 --- a/app/Services/Util.php +++ b/app/Services/Util.php @@ -2,6 +2,8 @@ namespace App\Services; +use Carbon\Carbon; + class Util { @@ -51,18 +53,17 @@ class Util public static function _format_date($date, $to = 'date') { if ($to === 'datetime') { - return \Carbon::parse($date)->format(\Util::formatDateTimeDB()); + return Carbon::parse($date)->format(self::formatDateTimeDB()); } - //date - return \Carbon::parse($date)->format(\Util::formatDateDB()); + return Carbon::parse($date)->format(self::formatDateDB()); } public static function _reformat_date($date, $to = 'date') { if ($to === 'datetime') { - return \Carbon::parse($date)->format('Y-m-d - H:i'); + return Carbon::parse($date)->format('Y-m-d - H:i'); } - return \Carbon::parse($date)->format('Y-m-d'); + return Carbon::parse($date)->format('Y-m-d'); } public static function _format_number($value) diff --git a/app/User.php b/app/User.php index 95d8aa6..5d40e29 100755 --- a/app/User.php +++ b/app/User.php @@ -11,7 +11,7 @@ use Illuminate\Support\Facades\Crypt; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; -use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions\F; +use Illuminate\Database\Eloquent\Factories\HasFactory; /** * App\User @@ -86,9 +86,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions\F; */ class User extends Authenticatable { - use HasApiTokens, Notifiable; - - use SoftDeletes; + use HasApiTokens, HasFactory, Notifiable, SoftDeletes; protected $connection = 'mysql'; diff --git a/boost.json b/boost.json new file mode 100644 index 0000000..0b66267 --- /dev/null +++ b/boost.json @@ -0,0 +1,11 @@ +{ + "agents": [ + "claude_code", + "cursor" + ], + "editors": [ + "claude_code", + "cursor" + ], + "guidelines": [] +} diff --git a/bootstrap/cache/config.php b/bootstrap/cache/config.php new file mode 100644 index 0000000..66c2e87 --- /dev/null +++ b/bootstrap/cache/config.php @@ -0,0 +1,1729 @@ + + array ( + 'name' => 'STERN TOURS CRM', + 'env' => 'local', + 'debug' => true, + 'url' => 'https://mein.sterntours.test', + 'old_url' => 'https://cms-stern-tours.test', + 'url_v2' => 'https://v2.sterntours.test', + 'url_stern' => 'https://sterntours.test', + 'domain_tld' => 'test', + 'timezone' => 'Europe/Berlin', + 'locale' => 'de', + 'fallback_locale' => 'de', + 'key' => 'base64:cxq+xNckU1xLwp8V9Bfj9+nOK5iZL6urcZ1EBO8usXg=', + 'cipher' => 'AES-256-CBC', + 'success_key' => 'f6077389c9ce710e554763a5de02c8ec', + 'providers' => + array ( + 0 => 'Illuminate\\Auth\\AuthServiceProvider', + 1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider', + 2 => 'Illuminate\\Bus\\BusServiceProvider', + 3 => 'Illuminate\\Cache\\CacheServiceProvider', + 4 => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 5 => 'Illuminate\\Cookie\\CookieServiceProvider', + 6 => 'Illuminate\\Database\\DatabaseServiceProvider', + 7 => 'Illuminate\\Encryption\\EncryptionServiceProvider', + 8 => 'Illuminate\\Filesystem\\FilesystemServiceProvider', + 9 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider', + 10 => 'Illuminate\\Hashing\\HashServiceProvider', + 11 => 'Illuminate\\Mail\\MailServiceProvider', + 12 => 'Illuminate\\Notifications\\NotificationServiceProvider', + 13 => 'Illuminate\\Pagination\\PaginationServiceProvider', + 14 => 'Illuminate\\Pipeline\\PipelineServiceProvider', + 15 => 'Illuminate\\Queue\\QueueServiceProvider', + 16 => 'Illuminate\\Redis\\RedisServiceProvider', + 17 => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider', + 18 => 'Illuminate\\Session\\SessionServiceProvider', + 19 => 'Illuminate\\Translation\\TranslationServiceProvider', + 20 => 'Illuminate\\Validation\\ValidationServiceProvider', + 21 => 'Illuminate\\View\\ViewServiceProvider', + 22 => 'Laravel\\Tinker\\TinkerServiceProvider', + 23 => 'Laravel\\Passport\\PassportServiceProvider', + 24 => 'App\\Providers\\AppServiceProvider', + 25 => 'App\\Providers\\AuthServiceProvider', + 26 => 'App\\Providers\\EventServiceProvider', + 27 => 'App\\Providers\\RouteServiceProvider', + 28 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', + 29 => 'Barryvdh\\DomPDF\\ServiceProvider', + 30 => 'Jenssegers\\Date\\DateServiceProvider', + 31 => 'Collective\\Html\\HtmlServiceProvider', + 32 => 'Intervention\\Image\\ImageServiceProvider', + 33 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 34 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 35 => 'Reliese\\Coders\\CodersServiceProvider', + ), + 'aliases' => + array ( + 'App' => 'Illuminate\\Support\\Facades\\App', + 'Arr' => 'Illuminate\\Support\\Arr', + 'Artisan' => 'Illuminate\\Support\\Facades\\Artisan', + 'Auth' => 'Illuminate\\Support\\Facades\\Auth', + 'Blade' => 'Illuminate\\Support\\Facades\\Blade', + 'Broadcast' => 'Illuminate\\Support\\Facades\\Broadcast', + 'Bus' => 'Illuminate\\Support\\Facades\\Bus', + 'Cache' => 'Illuminate\\Support\\Facades\\Cache', + 'Config' => 'Illuminate\\Support\\Facades\\Config', + 'Cookie' => 'Illuminate\\Support\\Facades\\Cookie', + 'Crypt' => 'Illuminate\\Support\\Facades\\Crypt', + 'DB' => 'Illuminate\\Support\\Facades\\DB', + 'Eloquent' => 'Illuminate\\Database\\Eloquent\\Model', + 'Event' => 'Illuminate\\Support\\Facades\\Event', + 'File' => 'Illuminate\\Support\\Facades\\File', + 'Gate' => 'Illuminate\\Support\\Facades\\Gate', + 'Hash' => 'Illuminate\\Support\\Facades\\Hash', + 'Lang' => 'Illuminate\\Support\\Facades\\Lang', + 'Log' => 'Illuminate\\Support\\Facades\\Log', + 'Mail' => 'Illuminate\\Support\\Facades\\Mail', + 'Notification' => 'Illuminate\\Support\\Facades\\Notification', + 'Password' => 'Illuminate\\Support\\Facades\\Password', + 'Queue' => 'Illuminate\\Support\\Facades\\Queue', + 'Redirect' => 'Illuminate\\Support\\Facades\\Redirect', + 'Redis' => 'Illuminate\\Support\\Facades\\Redis', + 'Request' => 'Illuminate\\Support\\Facades\\Request', + 'Response' => 'Illuminate\\Support\\Facades\\Response', + 'Route' => 'Illuminate\\Support\\Facades\\Route', + 'Schema' => 'Illuminate\\Support\\Facades\\Schema', + 'Session' => 'Illuminate\\Support\\Facades\\Session', + 'Storage' => 'Illuminate\\Support\\Facades\\Storage', + 'Str' => 'Illuminate\\Support\\Str', + 'URL' => 'Illuminate\\Support\\Facades\\URL', + 'Validator' => 'Illuminate\\Support\\Facades\\Validator', + 'View' => 'Illuminate\\Support\\Facades\\View', + 'Input' => 'Illuminate\\Support\\Facades\\Request', + 'Form' => 'Collective\\Html\\FormFacade', + 'Image' => 'Intervention\\Image\\Facades\\Image', + 'Carbon' => 'Carbon\\Carbon', + 'Date' => 'Jenssegers\\Date\\Date', + 'HTMLHelper' => 'App\\Helper\\HTMLHelper', + 'Util' => 'App\\Services\\Util', + 'Excel' => 'Maatwebsite\\Excel\\Facades\\Excel', + 'DataTables' => 'Yajra\\DataTables\\Facades\\DataTables', + 'PDF' => 'Barryvdh\\DomPDF\\Facade', + ), + ), + 'auth' => + array ( + 'defaults' => + array ( + 'guard' => 'web', + 'passwords' => 'users', + ), + 'guards' => + array ( + 'web' => + array ( + 'driver' => 'session', + 'provider' => 'users', + ), + 'api' => + array ( + 'driver' => 'passport', + 'provider' => 'users', + ), + ), + 'providers' => + array ( + 'users' => + array ( + 'driver' => 'eloquent', + 'model' => 'App\\User', + ), + ), + 'passwords' => + array ( + 'users' => + array ( + 'provider' => 'users', + 'table' => 'password_resets', + 'expire' => 60, + ), + ), + ), + 'booking' => + array ( + 'identifier_general' => 'booking-pdf-g-', + 'identifier_general_name' => 'booking-pdf-general-name', + 'identifier_content' => 'booking-pdf-c-', + 'identifier_content_name' => 'booking-pdf-content-name', + 'max_interval_days' => 28, + 'deposit_percentage_rate' => 25, + 'max_deposit_interval_days' => 28, + 'coupon_default_value' => '50,00', + 'coupon_valid_date_month' => 24, + ), + 'broadcasting' => + array ( + 'default' => 'log', + 'connections' => + array ( + 'pusher' => + array ( + 'driver' => 'pusher', + 'key' => '', + 'secret' => '', + 'app_id' => '', + 'options' => + array ( + 'cluster' => 'mt1', + 'encrypted' => true, + ), + ), + 'redis' => + array ( + 'driver' => 'redis', + 'connection' => 'default', + ), + 'log' => + array ( + 'driver' => 'log', + ), + 'null' => + array ( + 'driver' => 'null', + ), + ), + ), + 'cache' => + array ( + 'default' => 'file', + 'stores' => + array ( + 'apc' => + array ( + 'driver' => 'apc', + ), + 'array' => + array ( + 'driver' => 'array', + ), + 'database' => + array ( + 'driver' => 'database', + 'table' => 'cache', + 'connection' => NULL, + ), + 'file' => + array ( + 'driver' => 'file', + 'path' => '/workspace/mein.sterntours.de/storage/framework/cache/data', + ), + 'memcached' => + array ( + 'driver' => 'memcached', + 'persistent_id' => NULL, + 'sasl' => + array ( + 0 => NULL, + 1 => NULL, + ), + 'options' => + array ( + ), + 'servers' => + array ( + 0 => + array ( + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 100, + ), + ), + ), + 'redis' => + array ( + 'driver' => 'redis', + 'connection' => 'default', + ), + ), + 'prefix' => 'stern_tours_crm_cache', + ), + 'database' => + array ( + 'default' => 'mysql', + 'connections' => + array ( + 'sqlite' => + array ( + 'driver' => 'sqlite', + 'database' => 'stern_crm', + 'prefix' => '', + ), + 'mysql' => + array ( + 'driver' => 'mysql', + 'host' => 'global-mysql', + 'port' => '3306', + 'database' => 'stern_crm', + 'username' => 'root', + 'password' => 'password', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'strict' => true, + 'engine' => NULL, + ), + 'mysql_stern' => + array ( + 'driver' => 'mysql', + 'host' => 'global-mysql', + 'port' => '3306', + 'database' => 'stern_db', + 'username' => 'root', + 'password' => 'password', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'strict' => true, + 'engine' => NULL, + ), + 'pgsql' => + array ( + 'driver' => 'pgsql', + 'host' => 'global-mysql', + 'port' => '3306', + 'database' => 'stern_crm', + 'username' => 'root', + 'password' => 'password', + 'charset' => 'utf8', + 'prefix' => '', + 'schema' => 'public', + 'sslmode' => 'prefer', + ), + 'sqlsrv' => + array ( + 'driver' => 'sqlsrv', + 'host' => 'global-mysql', + 'port' => '3306', + 'database' => 'stern_crm', + 'username' => 'root', + 'password' => 'password', + 'charset' => 'utf8', + 'prefix' => '', + ), + ), + 'migrations' => 'migrations', + 'redis' => + array ( + 'client' => 'predis', + 'default' => + array ( + 'host' => 'global-redis', + 'password' => NULL, + 'port' => '6379', + 'database' => 0, + ), + ), + ), + 'datatables' => + array ( + 'search' => + array ( + 'smart' => true, + 'multi_term' => true, + 'case_insensitive' => true, + 'use_wildcards' => false, + ), + 'index_column' => 'DT_Row_Index', + 'engines' => + array ( + 'eloquent' => 'Yajra\\DataTables\\EloquentDataTable', + 'query' => 'Yajra\\DataTables\\QueryDataTable', + 'collection' => 'Yajra\\DataTables\\CollectionDataTable', + 'resource' => 'Yajra\\DataTables\\ApiResourceDataTable', + ), + 'builders' => + array ( + ), + 'nulls_last_sql' => '%s %s NULLS LAST', + 'error' => NULL, + 'columns' => + array ( + 'excess' => + array ( + 0 => 'rn', + 1 => 'row_num', + ), + 'escape' => '*', + 'raw' => + array ( + 0 => 'action', + ), + 'blacklist' => + array ( + 0 => 'password', + 1 => 'remember_token', + ), + 'whitelist' => '*', + ), + 'json' => + array ( + 'header' => + array ( + ), + 'options' => 0, + ), + 'callback' => + array ( + 0 => '$', + 1 => '$.', + 2 => 'function', + ), + ), + 'debugbar' => + array ( + 'enabled' => NULL, + 'hide_empty_tabs' => true, + 'except' => + array ( + 0 => 'telescope*', + ), + 'storage' => + array ( + 'enabled' => false, + 'driver' => 'file', + 'path' => '/workspace/mein.sterntours.de/storage/debugbar', + 'connection' => NULL, + 'provider' => '', + ), + 'editor' => 'phpstorm', + 'remote_sites_path' => NULL, + 'local_sites_path' => NULL, + 'include_vendors' => true, + 'capture_ajax' => false, + 'add_ajax_timing' => false, + 'ajax_handler_auto_show' => true, + 'ajax_handler_enable_tab' => true, + 'defer_datasets' => false, + 'error_handler' => false, + 'error_level' => 32767, + 'clockwork' => false, + 'collectors' => + array ( + 'phpinfo' => true, + 'messages' => true, + 'time' => true, + 'memory' => true, + 'exceptions' => true, + 'log' => true, + 'db' => true, + 'views' => true, + 'route' => true, + 'auth' => false, + 'gate' => true, + 'session' => true, + 'symfony_request' => true, + 'mail' => true, + 'laravel' => false, + 'events' => false, + 'default_request' => false, + 'logs' => false, + 'files' => false, + 'config' => false, + 'cache' => false, + 'models' => false, + ), + 'options' => + array ( + 'auth' => + array ( + 'show_name' => true, + ), + 'db' => + array ( + 'with_params' => true, + 'backtrace' => true, + 'timeline' => false, + 'explain' => + array ( + 'enabled' => false, + 'types' => + array ( + 0 => 'SELECT', + ), + ), + 'hints' => true, + ), + 'mail' => + array ( + 'full_log' => false, + ), + 'views' => + array ( + 'data' => false, + ), + 'route' => + array ( + 'label' => true, + ), + 'logs' => + array ( + 'file' => NULL, + ), + 'cache' => + array ( + 'values' => true, + ), + ), + 'inject' => true, + 'route_prefix' => '_debugbar', + 'route_middleware' => + array ( + ), + 'route_domain' => NULL, + 'theme' => 'auto', + 'debug_backtrace_limit' => 50, + ), + 'dompdf' => + array ( + 'show_warnings' => false, + 'public_path' => NULL, + 'convert_entities' => true, + 'options' => + array ( + 'font_dir' => '/workspace/mein.sterntours.de/storage/fonts', + 'font_cache' => '/workspace/mein.sterntours.de/storage/fonts', + 'temp_dir' => '/tmp', + 'chroot' => '/workspace/mein.sterntours.de', + 'allowed_protocols' => + array ( + 'file://' => + array ( + 'rules' => + array ( + ), + ), + 'http://' => + array ( + 'rules' => + array ( + ), + ), + 'https://' => + array ( + 'rules' => + array ( + ), + ), + ), + 'log_output_file' => NULL, + 'enable_font_subsetting' => false, + 'pdf_backend' => 'CPDF', + 'default_media_type' => 'screen', + 'default_paper_size' => 'a4', + 'default_paper_orientation' => 'portrait', + 'default_font' => 'serif', + 'dpi' => 96, + 'enable_php' => false, + 'enable_javascript' => true, + 'enable_remote' => true, + 'font_height_ratio' => 1.1, + 'enable_html5_parser' => true, + ), + 'orientation' => 'portrait', + 'defines' => + array ( + 'font_dir' => '/workspace/mein.sterntours.de/storage/fonts/', + 'font_cache' => '/workspace/mein.sterntours.de/storage/fonts/', + 'temp_dir' => '/tmp', + 'chroot' => '/workspace/mein.sterntours.de', + 'enable_font_subsetting' => false, + 'pdf_backend' => 'CPDF', + 'default_media_type' => 'print', + 'default_paper_size' => 'a4', + 'default_font' => 'sans-serif', + 'dpi' => 300, + 'enable_php' => false, + 'enable_javascript' => true, + 'enable_remote' => true, + 'font_height_ratio' => 0.8, + 'enable_html5_parser' => false, + ), + ), + 'excel' => + array ( + 'exports' => + array ( + 'chunk_size' => 1000, + 'pre_calculate_formulas' => false, + 'csv' => + array ( + 'delimiter' => ',', + 'enclosure' => '"', + 'line_ending' => ' +', + 'use_bom' => false, + 'include_separator_line' => false, + 'excel_compatibility' => false, + ), + ), + 'imports' => + array ( + 'read_only' => true, + 'heading_row' => + array ( + 'formatter' => 'slug', + ), + 'csv' => + array ( + 'delimiter' => ',', + 'enclosure' => '"', + 'escape_character' => '\\', + 'contiguous' => false, + 'input_encoding' => 'UTF-8', + ), + ), + 'extension_detector' => + array ( + 'xlsx' => 'Xlsx', + 'xlsm' => 'Xlsx', + 'xltx' => 'Xlsx', + 'xltm' => 'Xlsx', + 'xls' => 'Xls', + 'xlt' => 'Xls', + 'ods' => 'Ods', + 'ots' => 'Ods', + 'slk' => 'Slk', + 'xml' => 'Xml', + 'gnumeric' => 'Gnumeric', + 'htm' => 'Html', + 'html' => 'Html', + 'csv' => 'Csv', + 'tsv' => 'Csv', + 'pdf' => 'Dompdf', + ), + 'value_binder' => + array ( + 'default' => 'Maatwebsite\\Excel\\DefaultValueBinder', + ), + 'cache' => + array ( + 'driver' => 'memory', + 'batch' => + array ( + 'memory_limit' => 60000, + ), + 'illuminate' => + array ( + 'store' => NULL, + ), + 'default_ttl' => 10800, + ), + 'transactions' => + array ( + 'handler' => 'db', + ), + 'temporary_files' => + array ( + 'local_path' => '/tmp', + 'remote_disk' => NULL, + ), + ), + 'fewo' => + array ( + 'identifier_content' => 'fewo-pdf-general', + 'identifier_fewo' => 'fewo-pdf-', + ), + 'filesystems' => + array ( + 'default' => 'local', + 'cloud' => 's3', + 'disks' => + array ( + 'local' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app', + ), + 'public' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/public', + 'url' => 'https://mein.sterntours.test/storage', + 'visibility' => 'public', + ), + 'customer' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/customer', + 'url' => 'https://mein.sterntours.test/storage/customer', + 'visibility' => 'public', + ), + 'travel_user' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/travel_user', + 'url' => 'https://mein.sterntours.test/storage/travel_user', + 'visibility' => 'public', + ), + 'booking' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/booking', + 'url' => 'https://mein.sterntours.test/storage/booking', + 'visibility' => 'public', + ), + 'general' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/general', + 'url' => 'https://mein.sterntours.test/storage/general', + 'visibility' => 'public', + ), + 'booking_fewo' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/booking_fewo', + 'url' => 'https://mein.sterntours.test/storage/booking_fewo', + 'visibility' => 'public', + ), + 'lead' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/lead', + 'url' => 'https://mein.sterntours.test/storage/lead', + 'visibility' => 'public', + ), + 'fewo_invoices' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/fewo/invoices', + 'url' => 'https://mein.sterntours.test/storage/fewo/invoices', + 'visibility' => 'public', + ), + 'fewo_infos' => + array ( + 'driver' => 'local', + 'root' => '/workspace/mein.sterntours.de/storage/app/fewo/infos', + 'url' => 'https://mein.sterntours.test/storage/fewo/infos', + 'visibility' => 'public', + ), + 's3' => + array ( + 'driver' => 's3', + 'key' => NULL, + 'secret' => NULL, + 'region' => NULL, + 'bucket' => NULL, + 'url' => NULL, + ), + ), + ), + 'fpdf' => + array ( + 'orientation' => 'P', + 'unit' => 'mm', + 'size' => 'A4', + 'useVaporHeaders' => false, + ), + 'google2fa' => + array ( + 'enabled' => true, + 'lifetime' => 0, + 'keep_alive' => true, + 'auth' => 'auth', + 'guard' => '', + 'session_var' => 'google2fa', + 'otp_input' => 'one_time_password', + 'window' => 1, + 'forbid_old_passwords' => false, + 'otp_secret_column' => 'secret_key', + 'view' => 'auth.google2fa', + 'error_messages' => + array ( + 'wrong_otp' => 'Das \'One Time Password\' ist falsch.', + 'cannot_be_empty' => 'Das \'One Time Password\' kann nicht leer sein.', + 'unknown' => 'Ein unbekannter Fehler ist aufgetreten. Bitte versuche es erneut.', + ), + 'throw_exceptions' => true, + 'qrcode_image_backend' => 'svg', + ), + 'hashing' => + array ( + 'driver' => 'bcrypt', + 'bcrypt' => + array ( + 'rounds' => 10, + ), + 'argon' => + array ( + 'memory' => 1024, + 'threads' => 2, + 'time' => 2, + ), + ), + 'ide-helper' => + array ( + 'filename' => '_ide_helper', + 'models_filename' => '_ide_helper_models.php', + 'meta_filename' => '.phpstorm.meta.php', + 'include_fluent' => false, + 'include_factory_builders' => false, + 'write_model_magic_where' => true, + 'write_model_external_builder_methods' => true, + 'write_model_relation_count_properties' => true, + 'write_eloquent_model_mixins' => false, + 'include_helpers' => false, + 'helper_files' => + array ( + 0 => '/workspace/mein.sterntours.de/vendor/laravel/framework/src/Illuminate/Support/helpers.php', + ), + 'model_locations' => + array ( + 0 => 'app', + 1 => 'packages', + ), + 'ignored_models' => + array ( + ), + 'model_hooks' => + array ( + ), + 'extra' => + array ( + 'Eloquent' => + array ( + 0 => 'Illuminate\\Database\\Eloquent\\Builder', + 1 => 'Illuminate\\Database\\Query\\Builder', + ), + 'Session' => + array ( + 0 => 'Illuminate\\Session\\Store', + ), + ), + 'magic' => + array ( + 'Log' => + array ( + 'debug' => 'Monolog\\Logger::addDebug', + 'info' => 'Monolog\\Logger::addInfo', + 'notice' => 'Monolog\\Logger::addNotice', + 'warning' => 'Monolog\\Logger::addWarning', + 'error' => 'Monolog\\Logger::addError', + 'critical' => 'Monolog\\Logger::addCritical', + 'alert' => 'Monolog\\Logger::addAlert', + 'emergency' => 'Monolog\\Logger::addEmergency', + ), + ), + 'interfaces' => + array ( + ), + 'model_camel_case_properties' => false, + 'type_overrides' => + array ( + 'integer' => 'int', + 'boolean' => 'bool', + ), + 'include_class_docblocks' => false, + 'force_fqn' => false, + 'use_generics_annotations' => true, + 'additional_relation_types' => + array ( + ), + 'additional_relation_return_types' => + array ( + ), + 'post_migrate' => + array ( + ), + 'format' => 'php', + 'custom_db_types' => + array ( + ), + ), + 'image' => + array ( + 'driver' => 'gd', + ), + 'lfm' => + array ( + 'use_package_routes' => true, + 'allow_multi_user' => false, + 'allow_share_folder' => false, + 'user_folder_name' => 'IqContent\\LaravelFilemanager\\Handlers\\ConfigHandler', + 'shared_folder_name' => 'shares', + 'thumb_folder_name' => 'thumbs', + 'folder_categories' => + array ( + 'file' => + array ( + 'folder_name' => 'files', + 'startup_view' => 'grid', + 'max_size' => 50000, + 'valid_mime' => + array ( + 0 => 'image/jpeg', + 1 => 'image/pjpeg', + 2 => 'image/png', + 3 => 'image/gif', + 4 => 'image/svg+xml', + 5 => 'application/pdf', + 6 => 'text/plain', + 7 => 'application/msword', + 8 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 9 => 'application/vnd.ms-word.template.macroEnabled.12', + 10 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 11 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 12 => 'application/excel', + ), + ), + 'image' => + array ( + 'folder_name' => 'photos', + 'startup_view' => 'list', + 'max_size' => 50000, + 'valid_mime' => + array ( + 0 => 'image/jpeg', + 1 => 'image/pjpeg', + 2 => 'image/png', + 3 => 'image/gif', + 4 => 'image/svg+xml', + 5 => 'application/pdf', + 6 => 'text/plain', + ), + ), + ), + 'disk' => 'public', + 'rename_file' => false, + 'alphanumeric_filename' => true, + 'alphanumeric_directory' => true, + 'should_validate_size' => false, + 'should_validate_mime' => false, + 'create_folder_mode' => 493, + 'create_file_mode' => 420, + 'should_change_file_mode' => true, + 'over_write_on_duplicate' => false, + 'should_create_thumbnails' => true, + 'raster_mimetypes' => + array ( + 0 => 'image/jpeg', + 1 => 'image/pjpeg', + 2 => 'image/png', + ), + 'thumb_img_width' => 200, + 'thumb_img_height' => 200, + 'default_color' => '#ffc926', + 'resize_aspectRatio' => false, + 'resize_containment' => true, + 'file_type_array' => + array ( + 'pdf' => 'Adobe Acrobat', + 'doc' => 'Microsoft Word', + 'docx' => 'Microsoft Word', + 'xls' => 'Microsoft Excel', + 'xlsx' => 'Microsoft Excel', + 'zip' => 'Archive', + 'gif' => 'GIF Image', + 'jpg' => 'JPEG Image', + 'jpeg' => 'JPEG Image', + 'png' => 'PNG Image', + 'ppt' => 'Microsoft PowerPoint', + 'pptx' => 'Microsoft PowerPoint', + ), + 'file_icon_array' => + array ( + 'pdf' => 'fa-file-pdf', + 'doc' => 'fa-file-word', + 'docx' => 'fa-file-word', + 'xls' => 'fa-file-excel', + 'xlsx' => 'fa-file-excel', + 'zip' => 'fa-file-archive', + 'gif' => 'fa-file-image', + 'jpg' => 'fa-file-image', + 'jpeg' => 'fa-file-image', + 'png' => 'fa-file-image', + 'ppt' => 'fa-file-powerpoint', + 'pptx' => 'fa-file-powerpoint', + 'mp3' => 'fa-file-audio', + 'mp4' => 'fa-file-video', + 'txt' => 'fa-file-alt', + 'dwg' => 'fa-file-image', + 'youtube' => 'fab fa-youtube-square', + ), + 'php_ini_overrides' => + array ( + 'memory_limit' => '256M', + ), + ), + 'localization' => + array ( + 'supportedLocales' => + array ( + 'de' => + array ( + 'name' => 'German', + 'script' => 'Latn', + 'native' => 'Deutsch', + 'regional' => 'de_DE', + ), + ), + ), + 'logging' => + array ( + 'default' => 'stack', + 'channels' => + array ( + 'stack' => + array ( + 'driver' => 'stack', + 'channels' => + array ( + 0 => 'single', + ), + ), + 'single' => + array ( + 'driver' => 'single', + 'path' => '/workspace/mein.sterntours.de/storage/logs/laravel.log', + 'level' => 'debug', + ), + 'daily' => + array ( + 'driver' => 'daily', + 'path' => '/workspace/mein.sterntours.de/storage/logs/laravel.log', + 'level' => 'debug', + 'days' => 7, + ), + 'slack' => + array ( + 'driver' => 'slack', + 'url' => NULL, + 'username' => 'Laravel Log', + 'emoji' => ':boom:', + 'level' => 'critical', + ), + 'stderr' => + array ( + 'driver' => 'monolog', + 'handler' => 'Monolog\\Handler\\StreamHandler', + 'with' => + array ( + 'stream' => 'php://stderr', + ), + ), + 'syslog' => + array ( + 'driver' => 'syslog', + 'level' => 'debug', + ), + 'errorlog' => + array ( + 'driver' => 'errorlog', + 'level' => 'debug', + ), + 'browser' => + array ( + 'driver' => 'single', + 'path' => '/workspace/mein.sterntours.de/storage/logs/browser.log', + 'level' => 'debug', + 'days' => 14, + ), + ), + ), + 'mail' => + array ( + 'driver' => 'smtp', + 'host' => 'global-mailpit', + 'port' => '587', + 'from' => + array ( + 'address' => 'stern@sterntours.de', + 'name' => 'DEV Reisebüro STERN TOURS', + ), + 'mail_bbc' => + array ( + 0 => 'kevin@adametz.media', + ), + 'mail_fewo_employee' => 'kevin@adametz.media', + 'encryption' => 'TLS', + 'username' => 'stern@stern-tours.de', + 'password' => '13C!NlecB!Phil4beAxKl', + 'sendmail' => '/usr/sbin/sendmail -bs', + 'markdown' => + array ( + 'theme' => 'default', + 'paths' => + array ( + 0 => '/workspace/mein.sterntours.de/resources/views/vendor/mail', + ), + ), + ), + 'models' => + array ( + '*' => + array ( + 'path' => '/workspace/mein.sterntours.de/app/Models', + 'namespace' => 'App\\Models', + 'parent' => 'Illuminate\\Database\\Eloquent\\Model', + 'use' => + array ( + ), + 'connection' => false, + 'timestamps' => true, + 'soft_deletes' => true, + 'date_format' => 'Y-m-d H:i:s', + 'per_page' => 15, + 'base_files' => false, + 'snake_attributes' => true, + 'qualified_tables' => false, + 'hidden' => + array ( + 0 => '*secret*', + 1 => '*password', + 2 => '*token', + ), + 'guarded' => + array ( + ), + 'casts' => + array ( + '*_json' => 'json', + ), + 'except' => + array ( + 0 => 'migrations', + ), + ), + ), + 'permissions' => + array ( + 'groups' => + array ( + 0 => + array ( + 'my-dat' => + array ( + 'name' => 'Ihre Daten', + 'color' => 'client', + ), + ), + 1 => + array ( + 'crm' => + array ( + 'name' => 'ADMIN CRM ', + 'color' => 'admin', + ), + 'crm-tp' => + array ( + 'name' => 'ADMIN CRM > Reiseprogramme', + 'color' => 'admin', + ), + 'crm-tp-pr' => + array ( + 'name' => 'ADMIN CRM > Reiseprogramme > Programme', + 'color' => 'admin', + ), + 'crm-tp-dr' => + array ( + 'name' => 'ADMIN CRM > Reiseprogramme > Vorlagen', + 'color' => 'admin', + ), + 'crm-tp-tc' => + array ( + 'name' => 'ADMIN CRM > Reiseprogramme > Inhalte', + 'color' => 'admin', + ), + 'crm-bo' => + array ( + 'name' => 'ADMIN CRM > Buchungen', + 'color' => 'admin', + ), + 'crm-bo-re' => + array ( + 'name' => 'ADMIN CRM > Buchungen > Übersicht', + 'color' => 'admin', + ), + 'crm-bo-bo' => + array ( + 'name' => 'ADMIN CRM > Buchungen > Buchungen', + 'color' => 'admin', + ), + 'crm-bo-le' => + array ( + 'name' => 'ADMIN CRM > Buchungen > Anfragen', + 'color' => 'admin', + ), + 'crm-bo-cu' => + array ( + 'name' => 'ADMIN CRM > Buchungen > Kunden', + 'color' => 'admin', + ), + 'crm-cm' => + array ( + 'name' => 'ADMIN CRM > Kundenverwaltung', + 'color' => 'admin', + ), + 'crm-cm-cf' => + array ( + 'name' => 'ADMIN CRM > Kundenverwaltung > Kunden (FeWo)', + 'color' => 'admin', + ), + 'crm-cm-bf' => + array ( + 'name' => 'ADMIN CRM > Kundenverwaltung > Buchungen (FeWo)', + 'color' => 'admin', + ), + 'crm-mail' => + array ( + 'name' => 'ADMIN CRM > E-Mails', + 'color' => 'admin', + ), + 'crm-mail-le' => + array ( + 'name' => 'ADMIN CRM > E-Mails > Anfragen', + 'color' => 'admin', + ), + 'crm-mail-bo' => + array ( + 'name' => 'ADMIN CRM > E-Mails > Buchungen', + 'color' => 'admin', + ), + 'crm-mail-bf' => + array ( + 'name' => 'ADMIN CRM > E-Mails > Buchungen (Fewo)', + 'color' => 'admin', + ), + 'crm-iq-tl' => + array ( + 'name' => 'ADMIN CRM > Reisebausteine', + 'color' => 'admin', + ), + 'crm-iq-tl-pro' => + array ( + 'name' => 'ADMIN CRM > Reisebausteine > Programm', + 'color' => 'admin', + ), + 'crm-iq-tl-gp' => + array ( + 'name' => 'ADMIN CRM > Reisebausteine > Gruppe', + 'color' => 'admin', + ), + 'crm-iq-tl-it' => + array ( + 'name' => 'ADMIN CRM > Reisebausteine > Baustein', + 'color' => 'admin', + ), + 'crm-old-cm' => + array ( + 'name' => 'ADMIN CRM altes System > Kundenverwaltung', + 'color' => 'info', + ), + 'cms' => + array ( + 'name' => 'ADMIN CMS', + 'color' => 'secondary', + ), + 'cms-iq-assets' => + array ( + 'name' => 'ADMIN CMS > Medien', + 'color' => 'secondary', + ), + 'cms-tg' => + array ( + 'name' => 'ADMIN CMS > Reiseführer', + 'color' => 'secondary', + ), + 'cms-fewo' => + array ( + 'name' => 'ADMIN CMS > FeWo', + 'color' => 'secondary', + ), + 'cms-book' => + array ( + 'name' => 'ADMIN CMS > Buchungen', + 'color' => 'secondary', + ), + 'cms-fb' => + array ( + 'name' => 'ADMIN CMS > Feedback', + 'color' => 'secondary', + ), + 'cms-nw' => + array ( + 'name' => 'ADMIN CMS > News', + 'color' => 'secondary', + ), + 'cms-aq' => + array ( + 'name' => 'ADMIN CMS > Fragen & Antworten', + 'color' => 'secondary', + ), + 'cms-sb' => + array ( + 'name' => 'ADMIN CMS > Sidebar', + 'color' => 'secondary', + ), + 'cms-cn' => + array ( + 'name' => 'ADMIN CMS > Inhalte', + 'color' => 'secondary', + ), + 'cms-cn-in' => + array ( + 'name' => 'ADMIN CMS > Inhalte > Infos', + 'color' => 'secondary', + ), + 'cms-cn-al' => + array ( + 'name' => 'ADMIN CMS > Inhalte > Inhalte', + 'color' => 'secondary', + ), + 'cms-cn-au' => + array ( + 'name' => 'ADMIN CMS > Inhalte > Autor', + 'color' => 'secondary', + ), + 'cms-cn-co' => + array ( + 'name' => 'ADMIN CMS > Inhalte > Länder', + 'color' => 'secondary', + ), + 'cms-newsletter' => + array ( + 'name' => 'ADMIN CMS > Newsletter', + 'color' => 'secondary', + ), + 'crm-nav-api' => + array ( + 'name' => 'ADMIN CRM > Navigation API', + 'color' => 'secondary', + ), + ), + 2 => + array ( + 'sua-bo-n-edit' => + array ( + 'name' => 'SUPERADMIN > Buchungen > Notizen > bearbeiten', + 'color' => 'secondary', + ), + 'sua-fewo-n-edit' => + array ( + 'name' => 'SUPERADMIN > FeWo > Notizen > bearbeiten', + 'color' => 'secondary', + ), + 'sua-st' => + array ( + 'name' => 'SUPERADMIN > Einstellungen', + 'color' => 'superadmin', + ), + 'sua-st-al' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Airline', + 'color' => 'superadmin', + ), + 'sua-st-ap' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Airport', + 'color' => 'superadmin', + ), + 'sua-st-em' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > E-Mails', + 'color' => 'superadmin', + ), + 'sua-st-ke' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Keywords', + 'color' => 'superadmin', + ), + 'sua-st-sp' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Leistungsträger', + 'color' => 'superadmin', + ), + 'sua-st-tn' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Nationalitäten', + 'color' => 'superadmin', + ), + 'sua-st-co' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Reiseländer', + 'color' => 'superadmin', + ), + 'sua-st-tp' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Reiseprogramme', + 'color' => 'superadmin', + ), + 'sua-st-tpl' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Reiseorte', + 'color' => 'superadmin', + ), + 'sua-st-bs' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Reisestatus', + 'color' => 'superadmin', + ), + 'sua-st-tc' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Veranstalter', + 'color' => 'superadmin', + ), + 'sua-st-tca' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Reiseart', + 'color' => 'superadmin', + ), + 'sua-st-tap' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Zielflughafen', + 'color' => 'superadmin', + ), + 'sua-st-in' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Versicherungen', + 'color' => 'superadmin', + ), + 'sua-st-ca' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Kategorien', + 'color' => 'superadmin', + ), + 'sua-st-tgn' => + array ( + 'name' => 'SUPERADMIN > Einstellungen > Reisehinweise', + 'color' => 'superadmin', + ), + 'sua-re' => + array ( + 'name' => 'SUPERADMIN > Export', + 'color' => 'superadmin', + ), + 'sua-re-bo' => + array ( + 'name' => 'SUPERADMIN > Export > Buchungen', + 'color' => 'superadmin', + ), + 'sua-re-pp' => + array ( + 'name' => 'SUPERADMIN > Export > Leistungsträger', + 'color' => 'superadmin', + ), + 'sua-re-fw' => + array ( + 'name' => 'SUPERADMIN > Export > Fewo', + 'color' => 'superadmin', + ), + 'sua-re-le' => + array ( + 'name' => 'SUPERADMIN > Export > Anfragen', + 'color' => 'superadmin', + ), + 'sua-ur-rt' => + array ( + 'name' => 'SUPERADMIN > User Rechte', + 'color' => 'danger', + ), + ), + ), + 'roles' => + array ( + 0 => 'Kunde', + 1 => 'Admin', + 2 => 'SuperAdmin', + ), + ), + 'queue' => + array ( + 'default' => 'sync', + 'connections' => + array ( + 'sync' => + array ( + 'driver' => 'sync', + ), + 'database' => + array ( + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'retry_after' => 90, + ), + 'beanstalkd' => + array ( + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'retry_after' => 90, + ), + 'sqs' => + array ( + 'driver' => 'sqs', + 'key' => 'your-public-key', + 'secret' => 'your-secret-key', + 'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id', + 'queue' => 'your-queue-name', + 'region' => 'us-east-1', + ), + 'redis' => + array ( + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => 'default', + 'retry_after' => 90, + 'block_for' => NULL, + ), + ), + 'failed' => + array ( + 'database' => 'mysql', + 'table' => 'failed_jobs', + ), + ), + 'services' => + array ( + 'mailgun' => + array ( + 'domain' => NULL, + 'secret' => NULL, + ), + 'ses' => + array ( + 'key' => NULL, + 'secret' => NULL, + 'region' => 'us-east-1', + ), + 'sparkpost' => + array ( + 'secret' => NULL, + ), + 'stripe' => + array ( + 'model' => 'App\\User', + 'key' => NULL, + 'secret' => NULL, + ), + ), + 'session' => + array ( + 'driver' => 'file', + 'lifetime' => '120', + 'expire_on_close' => false, + 'encrypt' => false, + 'files' => '/workspace/mein.sterntours.de/storage/framework/sessions', + 'connection' => NULL, + 'table' => 'sessions', + 'store' => NULL, + 'lottery' => + array ( + 0 => 2, + 1 => 100, + ), + 'cookie' => 'stern_tours_crm_session', + 'path' => '/', + 'domain' => NULL, + 'secure' => false, + 'http_only' => true, + 'same_site' => NULL, + ), + 'tinker' => + array ( + 'commands' => + array ( + ), + 'alias' => + array ( + ), + 'dont_alias' => + array ( + ), + 'trust_project' => 'always', + ), + 'trustedproxy' => + array ( + 'proxies' => NULL, + 'headers' => 62, + ), + 'view' => + array ( + 'paths' => + array ( + 0 => '/workspace/mein.sterntours.de/resources/views', + ), + 'compiled' => '/workspace/mein.sterntours.de/storage/framework/views', + ), + 'cart' => + array ( + 'tax' => 10, + 'database' => + array ( + 'connection' => NULL, + 'table' => 'shoppingcart', + ), + 'destroy_on_logout' => false, + 'format' => + array ( + 'decimals' => 2, + 'decimal_point' => '.', + 'thousand_seperator' => '', + ), + 'discountOnFees' => false, + ), + 'boost' => + array ( + 'enabled' => true, + 'browser_logs_watcher' => true, + 'executable_paths' => + array ( + 'php' => NULL, + 'composer' => NULL, + 'npm' => NULL, + 'vendor_bin' => NULL, + ), + ), + 'mcp' => + array ( + 'redirect_domains' => + array ( + 0 => '*', + ), + ), + 'passport' => + array ( + 'guard' => 'web', + 'private_key' => NULL, + 'public_key' => NULL, + 'client_uuids' => false, + 'personal_access_client' => + array ( + 'id' => NULL, + 'secret' => NULL, + ), + ), + 'flare' => + array ( + 'key' => NULL, + 'flare_middleware' => + array ( + 0 => 'Spatie\\FlareClient\\FlareMiddleware\\RemoveRequestIp', + 1 => 'Spatie\\FlareClient\\FlareMiddleware\\AddGitInformation', + 2 => 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddNotifierName', + 3 => 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddEnvironmentInformation', + 4 => 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddExceptionInformation', + 5 => 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddDumps', + 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddLogs' => + array ( + 'maximum_number_of_collected_logs' => 200, + ), + 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddQueries' => + array ( + 'maximum_number_of_collected_queries' => 200, + 'report_query_bindings' => true, + ), + 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddJobs' => + array ( + 'max_chained_job_reporting_depth' => 5, + ), + 6 => 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddContext', + 7 => 'Spatie\\LaravelIgnition\\FlareMiddleware\\AddExceptionHandledStatus', + 'Spatie\\FlareClient\\FlareMiddleware\\CensorRequestBodyFields' => + array ( + 'censor_fields' => + array ( + 0 => 'password', + 1 => 'password_confirmation', + ), + ), + 'Spatie\\FlareClient\\FlareMiddleware\\CensorRequestHeaders' => + array ( + 'headers' => + array ( + 0 => 'API-KEY', + 1 => 'Authorization', + 2 => 'Cookie', + 3 => 'Set-Cookie', + 4 => 'X-CSRF-TOKEN', + 5 => 'X-XSRF-TOKEN', + ), + ), + ), + 'send_logs_as_events' => true, + ), + 'ignition' => + array ( + 'editor' => 'phpstorm', + 'theme' => 'auto', + 'enable_share_button' => true, + 'register_commands' => false, + 'solution_providers' => + array ( + 0 => 'Spatie\\Ignition\\Solutions\\SolutionProviders\\BadMethodCallSolutionProvider', + 1 => 'Spatie\\Ignition\\Solutions\\SolutionProviders\\MergeConflictSolutionProvider', + 2 => 'Spatie\\Ignition\\Solutions\\SolutionProviders\\UndefinedPropertySolutionProvider', + 3 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\IncorrectValetDbCredentialsSolutionProvider', + 4 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\MissingAppKeySolutionProvider', + 5 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\DefaultDbNameSolutionProvider', + 6 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\TableNotFoundSolutionProvider', + 7 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\MissingImportSolutionProvider', + 8 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\InvalidRouteActionSolutionProvider', + 9 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\ViewNotFoundSolutionProvider', + 10 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\RunningLaravelDuskInProductionProvider', + 11 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\MissingColumnSolutionProvider', + 12 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\UnknownValidationSolutionProvider', + 13 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\MissingMixManifestSolutionProvider', + 14 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\MissingViteManifestSolutionProvider', + 15 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\MissingLivewireComponentSolutionProvider', + 16 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\UndefinedViewVariableSolutionProvider', + 17 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\GenericLaravelExceptionSolutionProvider', + 18 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\OpenAiSolutionProvider', + 19 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\SailNetworkSolutionProvider', + 20 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\UnknownMysql8CollationSolutionProvider', + 21 => 'Spatie\\LaravelIgnition\\Solutions\\SolutionProviders\\UnknownMariadbCollationSolutionProvider', + ), + 'ignored_solution_providers' => + array ( + ), + 'enable_runnable_solutions' => NULL, + 'remote_sites_path' => '/workspace/mein.sterntours.de', + 'local_sites_path' => '', + 'housekeeping_endpoint_prefix' => '_ignition', + 'settings_file_path' => '', + 'recorders' => + array ( + 0 => 'Spatie\\LaravelIgnition\\Recorders\\DumpRecorder\\DumpRecorder', + 1 => 'Spatie\\LaravelIgnition\\Recorders\\JobRecorder\\JobRecorder', + 2 => 'Spatie\\LaravelIgnition\\Recorders\\LogRecorder\\LogRecorder', + 3 => 'Spatie\\LaravelIgnition\\Recorders\\QueryRecorder\\QueryRecorder', + ), + 'open_ai_key' => NULL, + 'with_stack_frame_arguments' => true, + 'argument_reducers' => + array ( + 0 => 'Spatie\\Backtrace\\Arguments\\Reducers\\BaseTypeArgumentReducer', + 1 => 'Spatie\\Backtrace\\Arguments\\Reducers\\ArrayArgumentReducer', + 2 => 'Spatie\\Backtrace\\Arguments\\Reducers\\StdClassArgumentReducer', + 3 => 'Spatie\\Backtrace\\Arguments\\Reducers\\EnumArgumentReducer', + 4 => 'Spatie\\Backtrace\\Arguments\\Reducers\\ClosureArgumentReducer', + 5 => 'Spatie\\Backtrace\\Arguments\\Reducers\\DateTimeArgumentReducer', + 6 => 'Spatie\\Backtrace\\Arguments\\Reducers\\DateTimeZoneArgumentReducer', + 7 => 'Spatie\\Backtrace\\Arguments\\Reducers\\SymphonyRequestArgumentReducer', + 8 => 'Spatie\\LaravelIgnition\\ArgumentReducers\\ModelArgumentReducer', + 9 => 'Spatie\\LaravelIgnition\\ArgumentReducers\\CollectionArgumentReducer', + 10 => 'Spatie\\Backtrace\\Arguments\\Reducers\\StringableArgumentReducer', + ), + ), + 'sluggable' => + array ( + 'source' => NULL, + 'maxLength' => NULL, + 'maxLengthKeepWords' => true, + 'method' => NULL, + 'separator' => '-', + 'unique' => true, + 'uniqueSuffix' => NULL, + 'firstUniqueSuffix' => 2, + 'includeTrashed' => false, + 'reserved' => NULL, + 'onUpdate' => false, + 'slugEngineOptions' => + array ( + ), + ), +); diff --git a/bootstrap/cache/packages.php b/bootstrap/cache/packages.php index 6d76fe3..ca9b227 100755 --- a/bootstrap/cache/packages.php +++ b/bootstrap/cache/packages.php @@ -38,31 +38,13 @@ ), 'digital-bird/shoppingcart' => array ( - 'aliases' => - array ( - 'Cart' => 'Gloudemans\\Shoppingcart\\Facades\\Cart', - ), 'providers' => array ( 0 => 'Gloudemans\\Shoppingcart\\ShoppingcartServiceProvider', ), - ), - 'facade/ignition' => - array ( 'aliases' => array ( - 'Flare' => 'Facade\\Ignition\\Facades\\Flare', - ), - 'providers' => - array ( - 0 => 'Facade\\Ignition\\IgnitionServiceProvider', - ), - ), - 'fideloper/proxy' => - array ( - 'providers' => - array ( - 0 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider', + 'Cart' => 'Gloudemans\\Shoppingcart\\Facades\\Cart', ), ), 'intervention/image' => @@ -108,6 +90,24 @@ 0 => 'Laracasts\\Flash\\FlashServiceProvider', ), ), + 'laravel/boost' => + array ( + 'providers' => + array ( + 0 => 'Laravel\\Boost\\BoostServiceProvider', + ), + ), + 'laravel/mcp' => + array ( + 'aliases' => + array ( + 'Mcp' => 'Laravel\\Mcp\\Server\\Facades\\Mcp', + ), + 'providers' => + array ( + 0 => 'Laravel\\Mcp\\Server\\McpServiceProvider', + ), + ), 'laravel/passport' => array ( 'providers' => @@ -115,6 +115,13 @@ 0 => 'Laravel\\Passport\\PassportServiceProvider', ), ), + 'laravel/roster' => + array ( + 'providers' => + array ( + 0 => 'Laravel\\Roster\\RosterServiceProvider', + ), + ), 'laravel/sail' => array ( 'providers' => @@ -173,6 +180,13 @@ 0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', ), ), + 'nunomaduro/termwind' => + array ( + 'providers' => + array ( + 0 => 'Termwind\\Laravel\\TermwindServiceProvider', + ), + ), 'pragmarx/google2fa-laravel' => array ( 'aliases' => @@ -191,6 +205,17 @@ 0 => 'Reliese\\Coders\\CodersServiceProvider', ), ), + 'spatie/laravel-ignition' => + array ( + 'aliases' => + array ( + 'Flare' => 'Spatie\\LaravelIgnition\\Facades\\Flare', + ), + 'providers' => + array ( + 0 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider', + ), + ), 'yajra/laravel-datatables-oracle' => array ( 'aliases' => diff --git a/bootstrap/cache/services.php b/bootstrap/cache/services.php index a8f761b..a5336f7 100755 --- a/bootstrap/cache/services.php +++ b/bootstrap/cache/services.php @@ -28,37 +28,40 @@ 24 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', 25 => 'Cviebrock\\EloquentSluggable\\ServiceProvider', 26 => 'Gloudemans\\Shoppingcart\\ShoppingcartServiceProvider', - 27 => 'Facade\\Ignition\\IgnitionServiceProvider', - 28 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider', - 29 => 'Intervention\\Image\\ImageServiceProvider', - 30 => 'IqContent\\LaravelFilemanager\\LaravelFilemanagerServiceProvider', - 31 => 'Jenssegers\\Date\\DateServiceProvider', - 32 => 'Laracasts\\Flash\\FlashServiceProvider', + 27 => 'Intervention\\Image\\ImageServiceProvider', + 28 => 'IqContent\\LaravelFilemanager\\LaravelFilemanagerServiceProvider', + 29 => 'Jenssegers\\Date\\DateServiceProvider', + 30 => 'Laracasts\\Flash\\FlashServiceProvider', + 31 => 'Laravel\\Boost\\BoostServiceProvider', + 32 => 'Laravel\\Mcp\\Server\\McpServiceProvider', 33 => 'Laravel\\Passport\\PassportServiceProvider', - 34 => 'Laravel\\Sail\\SailServiceProvider', - 35 => 'Laravel\\Tinker\\TinkerServiceProvider', - 36 => 'Laravel\\Ui\\UiServiceProvider', - 37 => 'Collective\\Html\\HtmlServiceProvider', - 38 => 'Maatwebsite\\Excel\\ExcelServiceProvider', - 39 => 'Carbon\\Laravel\\ServiceProvider', - 40 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', - 41 => 'PragmaRX\\Google2FALaravel\\ServiceProvider', - 42 => 'Reliese\\Coders\\CodersServiceProvider', - 43 => 'Yajra\\DataTables\\DataTablesServiceProvider', - 44 => 'Laravel\\Tinker\\TinkerServiceProvider', - 45 => 'Laravel\\Passport\\PassportServiceProvider', - 46 => 'App\\Providers\\AppServiceProvider', - 47 => 'App\\Providers\\AuthServiceProvider', - 48 => 'App\\Providers\\EventServiceProvider', - 49 => 'App\\Providers\\RouteServiceProvider', - 50 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', - 51 => 'Barryvdh\\DomPDF\\ServiceProvider', - 52 => 'Jenssegers\\Date\\DateServiceProvider', - 53 => 'Collective\\Html\\HtmlServiceProvider', - 54 => 'Intervention\\Image\\ImageServiceProvider', - 55 => 'Maatwebsite\\Excel\\ExcelServiceProvider', - 56 => 'Yajra\\DataTables\\DataTablesServiceProvider', - 57 => 'Reliese\\Coders\\CodersServiceProvider', + 34 => 'Laravel\\Roster\\RosterServiceProvider', + 35 => 'Laravel\\Sail\\SailServiceProvider', + 36 => 'Laravel\\Tinker\\TinkerServiceProvider', + 37 => 'Laravel\\Ui\\UiServiceProvider', + 38 => 'Collective\\Html\\HtmlServiceProvider', + 39 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 40 => 'Carbon\\Laravel\\ServiceProvider', + 41 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', + 42 => 'Termwind\\Laravel\\TermwindServiceProvider', + 43 => 'PragmaRX\\Google2FALaravel\\ServiceProvider', + 44 => 'Reliese\\Coders\\CodersServiceProvider', + 45 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider', + 46 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 47 => 'Laravel\\Tinker\\TinkerServiceProvider', + 48 => 'Laravel\\Passport\\PassportServiceProvider', + 49 => 'App\\Providers\\AppServiceProvider', + 50 => 'App\\Providers\\AuthServiceProvider', + 51 => 'App\\Providers\\EventServiceProvider', + 52 => 'App\\Providers\\RouteServiceProvider', + 53 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', + 54 => 'Barryvdh\\DomPDF\\ServiceProvider', + 55 => 'Jenssegers\\Date\\DateServiceProvider', + 56 => 'Collective\\Html\\HtmlServiceProvider', + 57 => 'Intervention\\Image\\ImageServiceProvider', + 58 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 59 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 60 => 'Reliese\\Coders\\CodersServiceProvider', ), 'eager' => array ( @@ -76,31 +79,34 @@ 11 => 'Barryvdh\\DomPDF\\ServiceProvider', 12 => 'Cviebrock\\EloquentSluggable\\ServiceProvider', 13 => 'Gloudemans\\Shoppingcart\\ShoppingcartServiceProvider', - 14 => 'Facade\\Ignition\\IgnitionServiceProvider', - 15 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider', - 16 => 'Intervention\\Image\\ImageServiceProvider', - 17 => 'IqContent\\LaravelFilemanager\\LaravelFilemanagerServiceProvider', - 18 => 'Jenssegers\\Date\\DateServiceProvider', - 19 => 'Laracasts\\Flash\\FlashServiceProvider', + 14 => 'Intervention\\Image\\ImageServiceProvider', + 15 => 'IqContent\\LaravelFilemanager\\LaravelFilemanagerServiceProvider', + 16 => 'Jenssegers\\Date\\DateServiceProvider', + 17 => 'Laracasts\\Flash\\FlashServiceProvider', + 18 => 'Laravel\\Boost\\BoostServiceProvider', + 19 => 'Laravel\\Mcp\\Server\\McpServiceProvider', 20 => 'Laravel\\Passport\\PassportServiceProvider', - 21 => 'Laravel\\Ui\\UiServiceProvider', - 22 => 'Maatwebsite\\Excel\\ExcelServiceProvider', - 23 => 'Carbon\\Laravel\\ServiceProvider', - 24 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', - 25 => 'PragmaRX\\Google2FALaravel\\ServiceProvider', - 26 => 'Reliese\\Coders\\CodersServiceProvider', - 27 => 'Yajra\\DataTables\\DataTablesServiceProvider', - 28 => 'Laravel\\Passport\\PassportServiceProvider', - 29 => 'App\\Providers\\AppServiceProvider', - 30 => 'App\\Providers\\AuthServiceProvider', - 31 => 'App\\Providers\\EventServiceProvider', - 32 => 'App\\Providers\\RouteServiceProvider', - 33 => 'Barryvdh\\DomPDF\\ServiceProvider', - 34 => 'Jenssegers\\Date\\DateServiceProvider', - 35 => 'Intervention\\Image\\ImageServiceProvider', - 36 => 'Maatwebsite\\Excel\\ExcelServiceProvider', - 37 => 'Yajra\\DataTables\\DataTablesServiceProvider', - 38 => 'Reliese\\Coders\\CodersServiceProvider', + 21 => 'Laravel\\Roster\\RosterServiceProvider', + 22 => 'Laravel\\Ui\\UiServiceProvider', + 23 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 24 => 'Carbon\\Laravel\\ServiceProvider', + 25 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', + 26 => 'Termwind\\Laravel\\TermwindServiceProvider', + 27 => 'PragmaRX\\Google2FALaravel\\ServiceProvider', + 28 => 'Reliese\\Coders\\CodersServiceProvider', + 29 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider', + 30 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 31 => 'Laravel\\Passport\\PassportServiceProvider', + 32 => 'App\\Providers\\AppServiceProvider', + 33 => 'App\\Providers\\AuthServiceProvider', + 34 => 'App\\Providers\\EventServiceProvider', + 35 => 'App\\Providers\\RouteServiceProvider', + 36 => 'Barryvdh\\DomPDF\\ServiceProvider', + 37 => 'Jenssegers\\Date\\DateServiceProvider', + 38 => 'Intervention\\Image\\ImageServiceProvider', + 39 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 40 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 41 => 'Reliese\\Coders\\CodersServiceProvider', ), 'deferred' => array ( @@ -111,99 +117,116 @@ 'Illuminate\\Contracts\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider', 'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => 'Illuminate\\Bus\\BusServiceProvider', 'Illuminate\\Bus\\BatchRepository' => 'Illuminate\\Bus\\BusServiceProvider', + 'Illuminate\\Bus\\DatabaseBatchRepository' => 'Illuminate\\Bus\\BusServiceProvider', 'cache' => 'Illuminate\\Cache\\CacheServiceProvider', 'cache.store' => 'Illuminate\\Cache\\CacheServiceProvider', 'cache.psr6' => 'Illuminate\\Cache\\CacheServiceProvider', 'memcached.connector' => 'Illuminate\\Cache\\CacheServiceProvider', 'Illuminate\\Cache\\RateLimiter' => 'Illuminate\\Cache\\CacheServiceProvider', - 'command.cache.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.cache.forget' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.clear-compiled' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.auth.resets.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.config.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.config.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\AboutCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Cache\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Cache\\Console\\ForgetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ClearCompiledCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Auth\\Console\\ClearResetsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ConfigCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ConfigClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ConfigShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'Illuminate\\Database\\Console\\DbCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.db.prune' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.db.wipe' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.down' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.environment' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.event.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.event.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.event.list' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.key.generate' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.optimize' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.optimize.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.package.discover' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.failed' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.flush' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.forget' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.listen' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.monitor' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.prune-batches' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.prune-failed-jobs' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.restart' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.retry' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.retry-batch' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.work' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.route.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.route.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.route.list' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.schema.dump' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.seed' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\PruneCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\ShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\WipeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\DownCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EnvironmentCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EnvironmentDecryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EnvironmentEncryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EventCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EventClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EventListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\KeyGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\OptimizeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\OptimizeClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\PackageDiscoverCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Cache\\Console\\PruneStaleTagsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\ListFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\FlushFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\ForgetFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\ListenCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\PruneBatchesCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\PruneFailedJobsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\RestartCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\RetryCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\RetryBatchCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\WorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\RouteCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\RouteClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\RouteListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\DumpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Seeds\\SeedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'Illuminate\\Console\\Scheduling\\ScheduleFinishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'Illuminate\\Console\\Scheduling\\ScheduleListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'Illuminate\\Console\\Scheduling\\ScheduleRunCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'Illuminate\\Console\\Scheduling\\ScheduleClearCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'Illuminate\\Console\\Scheduling\\ScheduleTestCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'Illuminate\\Console\\Scheduling\\ScheduleWorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.storage.link' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.up' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.view.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.view.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.cache.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.cast.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.channel.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.component.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.console.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.controller.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.event.generate' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.event.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.exception.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.factory.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.job.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.listener.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.mail.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.middleware.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.model.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.notification.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.notification.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.observer.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.policy.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.provider.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.failed-table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.queue.batches-table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.request.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.resource.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.rule.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.seeder.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.session.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.serve' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.stub.publish' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.test.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.vendor.publish' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Console\\Scheduling\\ScheduleInterruptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\ShowModelCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\StorageLinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\StorageUnlinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\UpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ViewCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ViewClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Cache\\Console\\CacheTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\CastMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ChannelListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ChannelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ComponentMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ConsoleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Routing\\Console\\ControllerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\DocsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EventGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\EventMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ExceptionMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Factories\\FactoryMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\JobMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\LangPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ListenerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\MailMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Routing\\Console\\MiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ModelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\NotificationMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Notifications\\Console\\NotificationTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ObserverMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\PolicyMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ProviderMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\FailedTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Queue\\Console\\BatchesTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\RequestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ResourceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\RuleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ScopeMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Seeds\\SeederMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Session\\Console\\SessionTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ServeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\StubPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\TestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\VendorPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Foundation\\Console\\ViewMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'migration.repository' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'migration.creator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate.fresh' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate.install' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate.refresh' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate.reset' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate.rollback' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate.status' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', - 'command.migrate.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\MigrateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\FreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\InstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\RefreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\ResetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\RollbackCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\StatusCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', + 'Illuminate\\Database\\Console\\Migrations\\MigrateMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'composer' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider', 'hash' => 'Illuminate\\Hashing\\HashServiceProvider', 'hash.driver' => 'Illuminate\\Hashing\\HashServiceProvider', @@ -211,6 +234,7 @@ 'mailer' => 'Illuminate\\Mail\\MailServiceProvider', 'Illuminate\\Mail\\Markdown' => 'Illuminate\\Mail\\MailServiceProvider', 'Illuminate\\Contracts\\Pipeline\\Hub' => 'Illuminate\\Pipeline\\PipelineServiceProvider', + 'pipeline' => 'Illuminate\\Pipeline\\PipelineServiceProvider', 'queue' => 'Illuminate\\Queue\\QueueServiceProvider', 'queue.connection' => 'Illuminate\\Queue\\QueueServiceProvider', 'queue.failer' => 'Illuminate\\Queue\\QueueServiceProvider', @@ -224,6 +248,7 @@ 'translation.loader' => 'Illuminate\\Translation\\TranslationServiceProvider', 'validator' => 'Illuminate\\Validation\\ValidationServiceProvider', 'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider', + 'Illuminate\\Contracts\\Validation\\UncompromisedVerifier' => 'Illuminate\\Validation\\ValidationServiceProvider', 'command.ide-helper.generate' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', 'command.ide-helper.models' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', 'Laravel\\Sail\\Console\\InstallCommand' => 'Laravel\\Sail\\SailServiceProvider', diff --git a/composer.json b/composer.json index 65281b0..c9828db 100755 --- a/composer.json +++ b/composer.json @@ -14,55 +14,90 @@ "options": { "symlink": true } + }, + { + "type": "package", + "package": { + "name": "laravelcollective/html", + "version": "6.5.0", + "description": "HTML and Form Builders for the Laravel Framework (L10+ patch)", + "license": "MIT", + "require": { + "php": "^8.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/routing": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/session": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/view": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "spatie/once": "^3.0" + }, + "autoload": { + "psr-4": { + "Collective\\Html\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": ["Collective\\Html\\HtmlServiceProvider"], + "aliases": { + "Form": "Collective\\Html\\FormFacade", + "Html": "Collective\\Html\\HtmlFacade" + } + } + }, + "source": { + "type": "git", + "url": "https://github.com/LaravelCollective/html.git", + "reference": "v6.4.1" + } + } } ], "require": { - "php": "^8.0|^8.2|^8.3", + "php": "^8.1|^8.2|^8.3", "bacon/bacon-qr-code": "^3.0", - "barryvdh/laravel-dompdf": "*", - "cviebrock/eloquent-sluggable": "*", + "barryvdh/laravel-dompdf": "^2.2", + "cviebrock/eloquent-sluggable": "^10.0", "digital-bird/shoppingcart": "^3.0", - "doctrine/dbal": "*", - "fideloper/proxy": "^4.4", - "guzzlehttp/guzzle": "^7.0.1", - "intervention/image": "*", + "doctrine/dbal": "^3.0", + "guzzlehttp/guzzle": "^7.0", + "intervention/image": "^2.7", "iqcontent/laravel-filemanager": "*", "jenssegers/date": "*", "laracasts/flash": "*", - "laravel/framework": "^8.12", + "laravel/framework": "^10.49", "laravel/helpers": "*", - "laravel/passport": "*", - "laravel/tinker": "^2.5", - "laravel/ui": "^3.1", - "laravelcollective/html": "*", + "laravel/passport": "^11.0", + "laravel/tinker": "^2.9", + "laravel/ui": "^4.0", + "laravelcollective/html": "^6.5", "maatwebsite/excel": "^3.1", - "pragmarx/google2fa-laravel": "^2.2", + "pragmarx/google2fa-laravel": "*", "reliese/laravel": "*", "rguedes/pdfmerger": "^1.0", "setasign/fpdf": "*", "setasign/fpdi": "*", - "yajra/laravel-datatables-oracle": "*" + "yajra/laravel-datatables-oracle": "^10.0" }, "require-dev": { - "facade/ignition": "^2.5", - "fakerphp/faker": "^1.9.1", - "laravel/sail": "^1.0.1", - "mockery/mockery": "^1.4.2", - "nunomaduro/collision": "^5.0", - "phpunit/phpunit": "^9.3.3", - "barryvdh/laravel-debugbar": "*", - "barryvdh/laravel-ide-helper": "*" + "barryvdh/laravel-debugbar": "^3.13", + "barryvdh/laravel-ide-helper": "^3.1", + "fakerphp/faker": "^1.23", + "laravel/boost": "^1.8", + "laravel/sail": "^1.29", + "mockery/mockery": "^1.6", + "nunomaduro/collision": "^7.0", + "phpunit/phpunit": "^10.5", + "spatie/laravel-ignition": "^2.0" }, "autoload": { "files": [ "app/helpers.php" ], - "classmap": [ - "database/seeds", - "database/factories" - ], "psr-4": { - "App\\": "app/" + "App\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeds/" } }, "autoload-dev": { @@ -77,11 +112,7 @@ }, "scripts": { "post-update-cmd": [ - "php artisan clear-compiled", - "Illuminate\\Foundation\\ComposerScripts::postUpdate", - "php artisan ide-helper:generate", - "php artisan ide-helper:meta", - "php artisan ide-helper:models" + "php artisan clear-compiled" ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" @@ -90,15 +121,17 @@ "@php artisan key:generate" ], "post-autoload-dump": [ - "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", - "@php artisan package:discover" + "@php artisan package:discover --ansi" ] }, "config": { "optimize-autoloader": true, "preferred-install": "dist", - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "php-http/discovery": true + } }, "minimum-stability": "dev", "prefer-stable": true -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 379627f..9d5af6e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b6cf4e9268d16ed0dbb6dfd2758d80be", + "content-hash": "477420f7285a4f55e4c9437d2ed8a9cd", "packages": [ { "name": "bacon/bacon-qr-code", - "version": "v3.0.1", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/Bacon/BaconQrCode.git", - "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f" + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/f9cc1f52b5a463062251d666761178dbdb6b544f", - "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", "shasum": "" }, "require": { @@ -27,8 +27,9 @@ }, "require-dev": { "phly/keep-a-changelog": "^2.12", - "phpunit/phpunit": "^10.5.11 || 11.0.4", + "phpunit/phpunit": "^10.5.11 || ^11.0.4", "spatie/phpunit-snapshot-assertions": "^5.1.5", + "spatie/pixelmatch-php": "^1.2.0", "squizlabs/php_codesniffer": "^3.9" }, "suggest": { @@ -56,9 +57,9 @@ "homepage": "https://github.com/Bacon/BaconQrCode", "support": { "issues": "https://github.com/Bacon/BaconQrCode/issues", - "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.1" + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.1.1" }, - "time": "2024-10-01T13:55:55+00:00" + "time": "2026-04-05T21:06:35+00:00" }, { "name": "barryvdh/laravel-dompdf", @@ -139,16 +140,16 @@ }, { "name": "brick/math", - "version": "0.13.1", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, "require": { @@ -187,7 +188,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" + "source": "https://github.com/brick/math/tree/0.12.3" }, "funding": [ { @@ -195,7 +196,7 @@ "type": "github" } ], - "time": "2025-03-29T13:50:30+00:00" + "time": "2025-02-28T13:11:00+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -268,21 +269,21 @@ }, { "name": "cocur/slugify", - "version": "v4.6.0", + "version": "v4.7.1", "source": { "type": "git", "url": "https://github.com/cocur/slugify.git", - "reference": "1d674022e9cbefa80b4f51aa3e2375b6e3c14fdb" + "reference": "a860dab2b9f5f37775fc6414d4f049434848165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cocur/slugify/zipball/1d674022e9cbefa80b4f51aa3e2375b6e3c14fdb", - "reference": "1d674022e9cbefa80b4f51aa3e2375b6e3c14fdb", + "url": "https://api.github.com/repos/cocur/slugify/zipball/a860dab2b9f5f37775fc6414d4f049434848165f", + "reference": "a860dab2b9f5f37775fc6414d4f049434848165f", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { "symfony/config": "<3.4 || >=4,<4.3", @@ -336,9 +337,9 @@ ], "support": { "issues": "https://github.com/cocur/slugify/issues", - "source": "https://github.com/cocur/slugify/tree/v4.6.0" + "source": "https://github.com/cocur/slugify/tree/v4.7.1" }, - "time": "2024-09-10T14:09:25+00:00" + "time": "2025-11-27T18:57:36+00:00" }, { "name": "composer/pcre", @@ -498,31 +499,30 @@ }, { "name": "cviebrock/eloquent-sluggable", - "version": "8.0.8", + "version": "10.0.0", "source": { "type": "git", "url": "https://github.com/cviebrock/eloquent-sluggable.git", - "reference": "16e21db24d80180f870c3c7c4faf3d3af23f4117" + "reference": "92f456b10337ca97c1cccfcc853a1cf51d2cedd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cviebrock/eloquent-sluggable/zipball/16e21db24d80180f870c3c7c4faf3d3af23f4117", - "reference": "16e21db24d80180f870c3c7c4faf3d3af23f4117", + "url": "https://api.github.com/repos/cviebrock/eloquent-sluggable/zipball/92f456b10337ca97c1cccfcc853a1cf51d2cedd0", + "reference": "92f456b10337ca97c1cccfcc853a1cf51d2cedd0", "shasum": "" }, "require": { - "cocur/slugify": "^4.0", - "illuminate/config": "^8.0", - "illuminate/database": "^8.0", - "illuminate/support": "^8.0", - "php": "^7.3|^8.0" + "cocur/slugify": "^4.3", + "illuminate/config": "^10.0", + "illuminate/database": "^10.0", + "illuminate/support": "^10.0", + "php": "^8.1" }, "require-dev": { "limedeck/phpunit-detailed-printer": "^6.0", - "mockery/mockery": "^1.4.2", - "orchestra/database": "^6.0", - "orchestra/testbench": "^6.0", - "phpunit/phpunit": "^9.4" + "mockery/mockery": "^1.4.4", + "orchestra/testbench": "^8.0", + "pestphp/pest": "2.x-dev" }, "type": "library", "extra": { @@ -559,7 +559,7 @@ ], "support": { "issues": "https://github.com/cviebrock/eloquent-sluggable/issues", - "source": "https://github.com/cviebrock/eloquent-sluggable/tree/8.0.8" + "source": "https://github.com/cviebrock/eloquent-sluggable/tree/10.0.0" }, "funding": [ { @@ -567,20 +567,20 @@ "type": "github" } ], - "time": "2021-06-12T01:05:33+00:00" + "time": "2023-02-16T23:01:35+00:00" }, { "name": "dasprid/enum", - "version": "1.0.6", + "version": "1.0.7", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", - "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", "shasum": "" }, "require": { @@ -615,9 +615,9 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" }, - "time": "2024-08-09T14:30:48+00:00" + "time": "2025-09-16T12:23:56+00:00" }, { "name": "defuse/php-encryption", @@ -763,22 +763,16 @@ }, { "name": "digital-bird/shoppingcart", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/digital-bird/LaravelShoppingcart.git", - "reference": "03f07813310df17368b1b3bfa15489ac9112b633" - }, + "version": "3.1.0", "dist": { - "type": "zip", - "url": "https://api.github.com/repos/digital-bird/LaravelShoppingcart/zipball/03f07813310df17368b1b3bfa15489ac9112b633", - "reference": "03f07813310df17368b1b3bfa15489ac9112b633", - "shasum": "" + "type": "path", + "url": "./packages/digital-bird/shoppingcart", + "reference": "e983be562e05c3543b54987a597b346a38a355fa" }, "require": { - "illuminate/events": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*|5.8.*|6.*|7.*|8.*", - "illuminate/session": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*|5.8.*|6.*|7.*|8.*", - "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*|5.8.*|6.*|7.*|8.*" + "illuminate/events": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/session": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" }, "require-dev": { "mockery/mockery": "~0.9.0", @@ -791,12 +785,12 @@ "type": "library", "extra": { "laravel": { - "aliases": { - "Cart": "Gloudemans\\Shoppingcart\\Facades\\Cart" - }, "providers": [ "Gloudemans\\Shoppingcart\\ShoppingcartServiceProvider" - ] + ], + "aliases": { + "Cart": "Gloudemans\\Shoppingcart\\Facades\\Cart" + } } }, "autoload": { @@ -804,7 +798,11 @@ "Gloudemans\\Shoppingcart\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "Gloudemans\\Tests\\Shoppingcart\\": "tests/" + } + }, "license": [ "MIT" ], @@ -819,23 +817,23 @@ "laravel", "shoppingcart" ], - "support": { - "source": "https://github.com/digital-bird/LaravelShoppingcart/tree/3.0.4" - }, - "time": "2020-10-23T07:09:33+00:00" + "transport-options": { + "symlink": true, + "relative": true + } }, { "name": "doctrine/dbal", - "version": "3.10.1", + "version": "3.10.5", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "3626601014388095d3af9de7e9e958623b7ef005" + "reference": "95d84866bf3c04b2ddca1df7c049714660959aef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/3626601014388095d3af9de7e9e958623b7ef005", - "reference": "3626601014388095d3af9de7e9e958623b7ef005", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/95d84866bf3c04b2ddca1df7c049714660959aef", + "reference": "95d84866bf3c04b2ddca1df7c049714660959aef", "shasum": "" }, "require": { @@ -851,16 +849,16 @@ }, "require-dev": { "doctrine/cache": "^1.11|^2.0", - "doctrine/coding-standard": "13.0.0", + "doctrine/coding-standard": "14.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "2.1.17", + "phpstan/phpstan": "2.1.30", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "9.6.23", - "slevomat/coding-standard": "8.16.2", - "squizlabs/php_codesniffer": "3.13.1", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/console": "^4.4|^5.4|^6.0|^7.0" + "phpunit/phpunit": "9.6.34", + "slevomat/coding-standard": "8.27.1", + "squizlabs/php_codesniffer": "4.0.1", + "symfony/cache": "^5.4|^6.0|^7.0|^8.0", + "symfony/console": "^4.4|^5.4|^6.0|^7.0|^8.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -920,7 +918,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.10.1" + "source": "https://github.com/doctrine/dbal/tree/3.10.5" }, "funding": [ { @@ -936,33 +934,33 @@ "type": "tidelift" } ], - "time": "2025-08-05T12:18:06+00:00" + "time": "2026-02-24T08:03:57+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -982,22 +980,22 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "doctrine/event-manager", - "version": "2.0.1", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf", "shasum": "" }, "require": { @@ -1007,10 +1005,10 @@ "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" + "doctrine/coding-standard": "^14", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/phpstan": "^2.1.32", + "phpunit/phpunit": "^10.5.58" }, "type": "library", "autoload": { @@ -1059,7 +1057,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.1" + "source": "https://github.com/doctrine/event-manager/tree/2.1.1" }, "funding": [ { @@ -1075,7 +1073,7 @@ "type": "tidelift" } ], - "time": "2024-05-22T20:47:39+00:00" + "time": "2026-01-29T07:11:08+00:00" }, { "name": "doctrine/inflector", @@ -1169,31 +1167,32 @@ }, { "name": "doctrine/lexer", - "version": "1.2.3", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.11" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1225,7 +1224,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.3" + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -1241,7 +1240,7 @@ "type": "tidelift" } ], - "time": "2022-02-28T11:07:21+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { "name": "dompdf/dompdf", @@ -1307,29 +1306,28 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.4.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "8c784d071debd117328803d86b2097615b457500" + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", - "reference": "8c784d071debd117328803d86b2097615b457500", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" + "php": "^8.2|^8.3|^8.4|^8.5" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" }, "type": "library", "extra": { @@ -1360,7 +1358,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" }, "funding": [ { @@ -1368,31 +1366,30 @@ "type": "github" } ], - "time": "2024-10-09T13:47:03+00:00" + "time": "2025-10-31T18:51:33+00:00" }, { "name": "egulias/email-validator", - "version": "2.1.25", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4" + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0dbf5d78455d4d6a41d186da50adc1122ec066f4", - "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", "shasum": "" }, "require": { - "doctrine/lexer": "^1.0.1", - "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.10" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "dominicsayers/isemail": "^3.0.7", - "phpunit/phpunit": "^4.8.36|^7.5.15", - "satooshi/php-coveralls": "^1.0.1" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -1400,7 +1397,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -1428,7 +1425,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/2.1.25" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" }, "funding": [ { @@ -1436,24 +1433,24 @@ "type": "github" } ], - "time": "2020-12-29T14:50:06+00:00" + "time": "2025-03-06T22:45:56+00:00" }, { "name": "ezyang/htmlpurifier", - "version": "v4.18.0", + "version": "v4.19.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "cb56001e54359df7ae76dc522d08845dc741621b" + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", - "reference": "cb56001e54359df7ae76dc522d08845dc741621b", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", "shasum": "" }, "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "cerdic/css-tidy": "^1.7 || ^2.0", @@ -1495,79 +1492,21 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" }, - "time": "2024-11-01T03:51:45+00:00" - }, - { - "name": "fideloper/proxy", - "version": "4.4.2", - "source": { - "type": "git", - "url": "https://github.com/fideloper/TrustedProxy.git", - "reference": "a751f2bc86dd8e6cfef12dc0cbdada82f5a18750" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/a751f2bc86dd8e6cfef12dc0cbdada82f5a18750", - "reference": "a751f2bc86dd8e6cfef12dc0cbdada82f5a18750", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0", - "php": ">=5.4.0" - }, - "require-dev": { - "illuminate/http": "^5.0|^6.0|^7.0|^8.0|^9.0", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^8.5.8|^9.3.3" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Fideloper\\Proxy\\TrustedProxyServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Fideloper\\Proxy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Fidao", - "email": "fideloper@gmail.com" - } - ], - "description": "Set trusted proxies for Laravel", - "keywords": [ - "load balancing", - "proxy", - "trusted proxy" - ], - "support": { - "issues": "https://github.com/fideloper/TrustedProxy/issues", - "source": "https://github.com/fideloper/TrustedProxy/tree/4.4.2" - }, - "time": "2022-02-09T13:33:34+00:00" + "time": "2025-10-17T16:34:55+00:00" }, { "name": "firebase/php-jwt", - "version": "v6.11.1", + "version": "6.x-dev", "source": { "type": "git", - "url": "https://github.com/firebase/php-jwt.git", + "url": "https://github.com/googleapis/php-jwt.git", "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", "shasum": "" }, @@ -1615,31 +1554,102 @@ "php" ], "support": { - "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + "issues": "https://github.com/googleapis/php-jwt/issues", + "source": "https://github.com/googleapis/php-jwt/tree/v6.11.1" }, "time": "2025-04-09T20:32:01+00:00" }, { - "name": "graham-campbell/result-type", - "version": "v1.1.3", + "name": "fruitcake/php-cors", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-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.4.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-12-03T09:33:47+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3" + "phpoption/phpoption": "^1.9.5" }, "require-dev": { - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" }, "type": "library", "autoload": { @@ -1668,7 +1678,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" }, "funding": [ { @@ -1680,7 +1690,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:45:45+00:00" + "time": "2025-12-27T19:43:20+00:00" }, { "name": "guzzlehttp/guzzle", @@ -1893,16 +1903,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.8.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "21dc724a0583619cd1652f673303492272778051" + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", - "reference": "21dc724a0583619cd1652f673303492272778051", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", "shasum": "" }, "require": { @@ -1918,6 +1928,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", + "jshttp/mime-db": "1.54.0.1", "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { @@ -1989,7 +2000,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.8.0" + "source": "https://github.com/guzzle/psr7/tree/2.9.0" }, "funding": [ { @@ -2005,7 +2016,93 @@ "type": "tidelift" } ], - "time": "2025-08-23T21:21:41+00:00" + "time": "2026-03-10T16:41:02+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "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.44 || ^9.6.25", + "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.5" + }, + "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": "2025-08-22T14:27:06+00:00" }, { "name": "intervention/image", @@ -2103,13 +2200,13 @@ "cviebrock/eloquent-sluggable": "*", "ext-exif": "*", "ext-fileinfo": "*", - "illuminate/config": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/container": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/filesystem": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/http": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/support": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", + "illuminate/config": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/filesystem": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "intervention/image": "2.*", - "php": ">=7.2.0" + "php": ">=8.1" }, "require-dev": { "mockery/mockery": "^0.9.9", @@ -2243,20 +2340,20 @@ }, { "name": "laracasts/flash", - "version": "3.2.4", + "version": "3.2.6", "source": { "type": "git", "url": "https://github.com/laracasts/flash.git", - "reference": "0ea47bfbf12a33113247c367798a34e519020f8d" + "reference": "4fc18fed3152d8910ed64589b0f5d049fd6a0806" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laracasts/flash/zipball/0ea47bfbf12a33113247c367798a34e519020f8d", - "reference": "0ea47bfbf12a33113247c367798a34e519020f8d", + "url": "https://api.github.com/repos/laracasts/flash/zipball/4fc18fed3152d8910ed64589b0f5d049fd6a0806", + "reference": "4fc18fed3152d8910ed64589b0f5d049fd6a0806", "shasum": "" }, "require": { - "illuminate/support": "~5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "~5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": ">=5.4.0" }, "require-dev": { @@ -2294,62 +2391,76 @@ ], "description": "Easy flash notifications", "support": { - "source": "https://github.com/laracasts/flash/tree/3.2.4" + "source": "https://github.com/laracasts/flash/tree/3.2.6" }, - "time": "2025-02-06T14:43:27+00:00" + "time": "2026-03-25T17:55:04+00:00" }, { "name": "laravel/framework", - "version": "v8.83.29", + "version": "10.50.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "d841a226a50c715431952a10260ba4fac9e91cc4" + "reference": "3ff39b7a9b83e633383ec9b019827ed54b6d38bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/d841a226a50c715431952a10260ba4fac9e91cc4", - "reference": "d841a226a50c715431952a10260ba4fac9e91cc4", + "url": "https://api.github.com/repos/laravel/framework/zipball/3ff39b7a9b83e633383ec9b019827ed54b6d38bc", + "reference": "3ff39b7a9b83e633383ec9b019827ed54b6d38bc", "shasum": "" }, "require": { - "doctrine/inflector": "^1.4|^2.0", - "dragonmantank/cron-expression": "^3.0.2", - "egulias/email-validator": "^2.1.10", - "ext-json": "*", + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.3.2", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", "ext-mbstring": "*", "ext-openssl": "*", - "laravel/serializable-closure": "^1.0", - "league/commonmark": "^1.3|^2.0.2", - "league/flysystem": "^1.1", - "monolog/monolog": "^2.0", - "nesbot/carbon": "^2.53.1", - "opis/closure": "^3.6", - "php": "^7.3|^8.0", - "psr/container": "^1.0", - "psr/log": "^1.0|^2.0", - "psr/simple-cache": "^1.0", - "ramsey/uuid": "^4.2.2", - "swiftmailer/swiftmailer": "^6.3", - "symfony/console": "^5.4", - "symfony/error-handler": "^5.4", - "symfony/finder": "^5.4", - "symfony/http-foundation": "^5.4", - "symfony/http-kernel": "^5.4", - "symfony/mime": "^5.4", - "symfony/process": "^5.4", - "symfony/routing": "^5.4", - "symfony/var-dumper": "^5.4", - "tijsverkoyen/css-to-inline-styles": "^2.2.2", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", + "laravel/serializable-closure": "^1.3", + "league/commonmark": "^2.2.1", + "league/flysystem": "^3.8.0", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.67", + "nunomaduro/termwind": "^1.13", + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^6.2", + "symfony/error-handler": "^6.2", + "symfony/finder": "^6.2", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.2", + "symfony/mailer": "^6.2", + "symfony/mime": "^6.2", + "symfony/process": "^6.2", + "symfony/routing": "^6.2", + "symfony/uid": "^6.2", + "symfony/var-dumper": "^6.2", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", "vlucas/phpdotenv": "^5.4.1", - "voku/portable-ascii": "^1.6.1" + "voku/portable-ascii": "^2.0" }, "conflict": { + "carbonphp/carbon-doctrine-types": ">=3.0", + "doctrine/dbal": ">=4.0", + "mockery/mockery": "1.6.8", + "phpunit/phpunit": ">=11.0.0", "tightenco/collect": "<5.5.33" }, "provide": { - "psr/container-implementation": "1.0", - "psr/simple-cache-implementation": "1.0" + "psr/container-implementation": "1.1|2.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" }, "replace": { "illuminate/auth": "self.version", @@ -2357,6 +2468,7 @@ "illuminate/bus": "self.version", "illuminate/cache": "self.version", "illuminate/collections": "self.version", + "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", "illuminate/container": "self.version", @@ -2374,6 +2486,7 @@ "illuminate/notifications": "self.version", "illuminate/pagination": "self.version", "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", "illuminate/routing": "self.version", @@ -2385,59 +2498,77 @@ "illuminate/view": "self.version" }, "require-dev": { - "aws/aws-sdk-php": "^3.198.1", - "doctrine/dbal": "^2.13.3|^3.1.4", - "filp/whoops": "^2.14.3", - "guzzlehttp/guzzle": "^6.5.5|^7.0.1", - "league/flysystem-cached-adapter": "^1.0", - "mockery/mockery": "^1.4.4", - "orchestra/testbench-core": "^6.27", + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^3.5.1", + "ext-gmp": "*", + "fakerphp/faker": "^1.21", + "guzzlehttp/guzzle": "^7.5", + "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-path-prefixing": "^3.3", + "league/flysystem-read-only": "^3.3", + "league/flysystem-sftp-v3": "^3.0", + "mockery/mockery": "^1.5.1", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.23.4", "pda/pheanstalk": "^4.0", - "phpunit/phpunit": "^8.5.19|^9.5.8", - "predis/predis": "^1.1.9", - "symfony/cache": "^5.4" + "phpstan/phpstan": "~1.11.11", + "phpunit/phpunit": "^10.0.7", + "predis/predis": "^2.0.2", + "symfony/cache": "^6.2", + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.198.1).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).", - "ext-bcmath": "Required to use the multiple_of validation rule.", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", "ext-memcached": "Required to use the memcache cache driver.", - "ext-pcntl": "Required to use all features of the queue worker.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", - "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", - "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", - "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", - "mockery/mockery": "Required to use mocking (^1.4.4).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", + "league/flysystem-read-only": "Required to use read-only disks (^3.3)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "mockery/mockery": "Required to use mocking (^1.5.1).", "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "phpunit/phpunit": "Required to use assertions and run tests (^8.5.19|^9.5.8).", - "predis/predis": "Required to use the predis connector (^1.1.9).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", + "predis/predis": "Required to use the predis connector (^2.0.2).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0|^7.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^5.4).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^5.4).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", - "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { "files": [ + "src/Illuminate/Collections/functions.php", "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Support/helpers.php" ], @@ -2445,7 +2576,8 @@ "Illuminate\\": "src/Illuminate/", "Illuminate\\Support\\": [ "src/Illuminate/Macroable/", - "src/Illuminate/Collections/" + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" ] } }, @@ -2469,29 +2601,29 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-11-20T15:55:41+00:00" + "time": "2026-02-15T14:12:07+00:00" }, { "name": "laravel/helpers", - "version": "v1.8.0", + "version": "v1.8.3", "source": { "type": "git", "url": "https://github.com/laravel/helpers.git", - "reference": "97738f7282cdac8d261468ad214d80e88042edd3" + "reference": "5915be977c7cc05fe2498d561b8c026ee56567dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/helpers/zipball/97738f7282cdac8d261468ad214d80e88042edd3", - "reference": "97738f7282cdac8d261468ad214d80e88042edd3", + "url": "https://api.github.com/repos/laravel/helpers/zipball/5915be977c7cc05fe2498d561b8c026ee56567dd", + "reference": "5915be977c7cc05fe2498d561b8c026ee56567dd", "shasum": "" }, "require": { - "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^7.2.0|^8.0" }, "require-dev": { "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^7.0|^8.0|^9.0|^10.0" + "phpunit/phpunit": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" }, "type": "library", "extra": { @@ -2524,46 +2656,47 @@ "laravel" ], "support": { - "source": "https://github.com/laravel/helpers/tree/v1.8.0" + "source": "https://github.com/laravel/helpers/tree/v1.8.3" }, - "time": "2025-09-01T19:53:58+00:00" + "time": "2026-03-17T16:40:11+00:00" }, { "name": "laravel/passport", - "version": "v10.4.2", + "version": "v11.10.6", "source": { "type": "git", "url": "https://github.com/laravel/passport.git", - "reference": "4bfdb9610575a0c84a6810701f4fd45fb8ab3888" + "reference": "2642f360c51dfde3a6ea60f86ae5d9a8c0caf3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/passport/zipball/4bfdb9610575a0c84a6810701f4fd45fb8ab3888", - "reference": "4bfdb9610575a0c84a6810701f4fd45fb8ab3888", + "url": "https://api.github.com/repos/laravel/passport/zipball/2642f360c51dfde3a6ea60f86ae5d9a8c0caf3cf", + "reference": "2642f360c51dfde3a6ea60f86ae5d9a8c0caf3cf", "shasum": "" }, "require": { "ext-json": "*", - "firebase/php-jwt": "^6.0", - "illuminate/auth": "^8.37|^9.0", - "illuminate/console": "^8.37|^9.0", - "illuminate/container": "^8.37|^9.0", - "illuminate/contracts": "^8.37|^9.0", - "illuminate/cookie": "^8.37|^9.0", - "illuminate/database": "^8.37|^9.0", - "illuminate/encryption": "^8.37|^9.0", - "illuminate/http": "^8.37|^9.0", - "illuminate/support": "^8.37|^9.0", - "lcobucci/jwt": "^3.4|^4.0", - "league/oauth2-server": "^8.2", - "nyholm/psr7": "^1.3", - "php": "^7.3|^8.0", + "firebase/php-jwt": "^6.4", + "illuminate/auth": "^9.0|^10.0", + "illuminate/console": "^9.0|^10.0", + "illuminate/container": "^9.0|^10.0", + "illuminate/contracts": "^9.0|^10.0", + "illuminate/cookie": "^9.0|^10.0", + "illuminate/database": "^9.0|^10.0", + "illuminate/encryption": "^9.0|^10.0", + "illuminate/http": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "lcobucci/jwt": "^4.3|^5.0", + "league/oauth2-server": "^8.5.3", + "nyholm/psr7": "^1.5", + "php": "^8.0", "phpseclib/phpseclib": "^2.0|^3.0", - "symfony/psr-http-message-bridge": "^2.0" + "symfony/psr-http-message-bridge": "^2.1" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^6.0|^7.0", + "orchestra/testbench": "^7.31|^8.11", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -2574,7 +2707,7 @@ ] }, "branch-alias": { - "dev-master": "10.x-dev" + "dev-master": "11.x-dev" } }, "autoload": { @@ -2603,7 +2736,65 @@ "issues": "https://github.com/laravel/passport/issues", "source": "https://github.com/laravel/passport" }, - "time": "2023-02-21T07:47:20+00:00" + "time": "2024-03-01T11:11:18+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.25", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.25" + }, + "time": "2024-08-12T22:06:33+00:00" }, { "name": "laravel/serializable-closure", @@ -2668,16 +2859,16 @@ }, { "name": "laravel/tinker", - "version": "v2.10.1", + "version": "v2.11.1", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741", + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741", "shasum": "" }, "require": { @@ -2686,7 +2877,7 @@ "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "php": "^7.2.5|^8.0", "psy/psysh": "^0.11.1|^0.12.0", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", @@ -2728,33 +2919,35 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.1" + "source": "https://github.com/laravel/tinker/tree/v2.11.1" }, - "time": "2025-01-27T14:24:01+00:00" + "time": "2026-02-06T14:12:35+00:00" }, { "name": "laravel/ui", - "version": "v3.4.6", + "version": "v4.6.3", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "65ec5c03f7fee2c8ecae785795b829a15be48c2c" + "reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/65ec5c03f7fee2c8ecae785795b829a15be48c2c", - "reference": "65ec5c03f7fee2c8ecae785795b829a15be48c2c", + "url": "https://api.github.com/repos/laravel/ui/zipball/ff27db15416c1ed8ad9848f5692e47595dd5de27", + "reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27", "shasum": "" }, "require": { - "illuminate/console": "^8.42|^9.0", - "illuminate/filesystem": "^8.42|^9.0", - "illuminate/support": "^8.82|^9.0", - "illuminate/validation": "^8.42|^9.0", - "php": "^7.3|^8.0" + "illuminate/console": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/validation": "^9.21|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0|^8.0" }, "require-dev": { - "orchestra/testbench": "^6.23|^7.0" + "orchestra/testbench": "^7.35|^8.15|^9.0|^10.0|^11.0", + "phpunit/phpunit": "^9.3|^10.4|^11.5|^12.5|^13.0" }, "type": "library", "extra": { @@ -2764,7 +2957,7 @@ ] }, "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { @@ -2789,42 +2982,29 @@ "ui" ], "support": { - "source": "https://github.com/laravel/ui/tree/v3.4.6" + "source": "https://github.com/laravel/ui/tree/v4.6.3" }, - "time": "2022-05-20T13:38:08+00:00" + "time": "2026-03-17T13:41:52+00:00" }, { "name": "laravelcollective/html", - "version": "v6.4.1", + "version": "6.5.0", "source": { "type": "git", "url": "https://github.com/LaravelCollective/html.git", - "reference": "64ddfdcaeeb8d332bd98bef442bef81e39c3910b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/LaravelCollective/html/zipball/64ddfdcaeeb8d332bd98bef442bef81e39c3910b", - "reference": "64ddfdcaeeb8d332bd98bef442bef81e39c3910b", - "shasum": "" + "reference": "v6.4.1" }, "require": { - "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/routing": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/session": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/view": "^6.0|^7.0|^8.0|^9.0|^10.0", - "php": ">=7.2.5" - }, - "require-dev": { - "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0", - "mockery/mockery": "~1.0", - "phpunit/phpunit": "~8.5|^9.5.10" + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/routing": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/session": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/view": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^8.0", + "spatie/once": "^3.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "6.x-dev" - }, "laravel": { "providers": [ "Collective\\Html\\HtmlServiceProvider" @@ -2836,66 +3016,45 @@ } }, "autoload": { - "files": [ - "src/helpers.php" - ], "psr-4": { "Collective\\Html\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Adam Engebretson", - "email": "adam@laravelcollective.com" - }, - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "description": "HTML and Form Builders for the Laravel Framework", - "homepage": "https://laravelcollective.com", - "support": { - "issues": "https://github.com/LaravelCollective/html/issues", - "source": "https://github.com/LaravelCollective/html" - }, - "abandoned": "spatie/laravel-html", - "time": "2023-04-25T02:46:11+00:00" + "description": "HTML and Form Builders for the Laravel Framework (L10+ patch)" }, { "name": "lcobucci/clock", - "version": "3.3.1", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b" + "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b", - "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/a3139d9e97d47826f27e6a17bb63f13621f86058", + "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "infection/infection": "^0.29", - "lcobucci/coding-standard": "^11.1.0", + "infection/infection": "^0.31", + "lcobucci/coding-standard": "^11.2.0", "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.10.25", - "phpstan/phpstan-deprecation-rules": "^1.1.3", - "phpstan/phpstan-phpunit": "^1.3.13", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^11.3.6" + "phpstan/phpstan": "^2.0.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0.0", + "phpstan/phpstan-strict-rules": "^2.0.0", + "phpunit/phpunit": "^12.0.0" }, "type": "library", "autoload": { @@ -2916,7 +3075,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.3.1" + "source": "https://github.com/lcobucci/clock/tree/3.5.0" }, "funding": [ { @@ -2928,43 +3087,42 @@ "type": "patreon" } ], - "time": "2024-09-24T20:45:14+00:00" + "time": "2025-10-27T09:03:17+00:00" }, { "name": "lcobucci/jwt", - "version": "4.3.0", + "version": "5.6.0", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4" + "reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4", - "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/bb3e9f21e4196e8afc41def81ef649c164bca25e", + "reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e", "shasum": "" }, "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mbstring": "*", "ext-openssl": "*", "ext-sodium": "*", - "lcobucci/clock": "^2.0 || ^3.0", - "php": "^7.4 || ^8.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/clock": "^1.0" }, "require-dev": { - "infection/infection": "^0.21", - "lcobucci/coding-standard": "^6.0", - "mikey179/vfsstream": "^1.6.7", + "infection/infection": "^0.29", + "lcobucci/clock": "^3.2", + "lcobucci/coding-standard": "^11.0", "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/php-invoker": "^3.1", - "phpunit/phpunit": "^9.5" + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^11.1" + }, + "suggest": { + "lcobucci/clock": ">= 3.2" }, "type": "library", "autoload": { @@ -2990,7 +3148,7 @@ ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/4.3.0" + "source": "https://github.com/lcobucci/jwt/tree/5.6.0" }, "funding": [ { @@ -3002,20 +3160,20 @@ "type": "patreon" } ], - "time": "2023-01-02T13:28:00+00:00" + "time": "2025-10-17T11:30:53+00:00" }, { "name": "league/commonmark", - "version": "2.7.1", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" + "reference": "59fb075d2101740c337c7216e3f32b36c204218b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", - "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b", "shasum": "" }, "require": { @@ -3040,9 +3198,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 | ^7.0", - "symfony/process": "^5.4 | ^6.0 | ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, @@ -3052,7 +3210,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.8-dev" + "dev-main": "2.9-dev" } }, "autoload": { @@ -3109,7 +3267,7 @@ "type": "tidelift" } ], - "time": "2025-07-20T12:47:49+00:00" + "time": "2026-03-19T13:16:38+00:00" }, { "name": "league/config", @@ -3249,54 +3407,55 @@ }, { "name": "league/flysystem", - "version": "1.1.10", + "version": "3.33.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1" + "reference": "570b8871e0ce693764434b29154c54b434905350" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1", - "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/570b8871e0ce693764434b29154c54b434905350", + "reference": "570b8871e0ce693764434b29154c54b434905350", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "league/mime-type-detection": "^1.3", - "php": "^7.2.5 || ^8.0" + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" }, "conflict": { - "league/flysystem-sftp": "<1.0.6" + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" }, "require-dev": { - "phpspec/prophecy": "^1.11.1", - "phpunit/phpunit": "^8.5.8" - }, - "suggest": { - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "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|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { "psr-4": { - "League\\Flysystem\\": "src/" + "League\\Flysystem\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3306,40 +3465,77 @@ "authors": [ { "name": "Frank de Jonge", - "email": "info@frenky.net" + "email": "info@frankdejonge.nl" } ], - "description": "Filesystem abstraction: Many filesystems, one API.", + "description": "File storage abstraction for PHP", "keywords": [ - "Cloud Files", "WebDAV", - "abstraction", "aws", "cloud", - "copy.com", - "dropbox", - "file systems", + "file", "files", "filesystem", "filesystems", "ftp", - "rackspace", - "remote", "s3", "sftp", "storage" ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/1.1.10" + "source": "https://github.com/thephpleague/flysystem/tree/3.33.0" }, - "funding": [ + "time": "2026-03-25T07:59:30+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.31.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://offset.earth/frankdejonge", - "type": "other" + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" } ], - "time": "2022-10-04T09:16:37+00:00" + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" + }, + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/mime-type-detection", @@ -3487,33 +3683,38 @@ }, { "name": "league/uri", - "version": "7.5.1", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "81fb5145d2644324614cc532b28efd0215bda430" + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", - "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.5", - "php": "^8.1" + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" }, "conflict": { "league/uri-schemes": "^1.0" }, "suggest": { "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", "ext-fileinfo": "to create Data URI from file contennts", "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", - "league/uri-components": "Needed to easily manipulate URI objects components", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3541,6 +3742,7 @@ "description": "URI manipulation library", "homepage": "https://uri.thephpleague.com", "keywords": [ + "URN", "data-uri", "file-uri", "ftp", @@ -3553,9 +3755,11 @@ "psr-7", "query-string", "querystring", + "rfc2141", "rfc3986", "rfc3987", "rfc6570", + "rfc8141", "uri", "uri-template", "url", @@ -3565,7 +3769,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.5.1" + "source": "https://github.com/thephpleague/uri/tree/7.8.1" }, "funding": [ { @@ -3573,26 +3777,25 @@ "type": "github" } ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2026-03-15T20:22:25+00:00" }, { "name": "league/uri-interfaces", - "version": "7.5.0", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", "shasum": "" }, "require": { "ext-filter": "*", "php": "^8.1", - "psr/http-factory": "^1", "psr/http-message": "^1.1 || ^2.0" }, "suggest": { @@ -3600,6 +3803,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3624,7 +3828,7 @@ "homepage": "https://nyamsprod.com" } ], - "description": "Common interfaces and classes for URI representation and interaction", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", "homepage": "https://uri.thephpleague.com", "keywords": [ "data-uri", @@ -3649,7 +3853,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" }, "funding": [ { @@ -3657,33 +3861,33 @@ "type": "github" } ], - "time": "2024-12-08T08:18:47+00:00" + "time": "2026-03-08T20:05:35+00:00" }, { "name": "maatwebsite/excel", - "version": "3.1.67", + "version": "3.1.68", "source": { "type": "git", "url": "https://github.com/SpartnerNL/Laravel-Excel.git", - "reference": "e508e34a502a3acc3329b464dad257378a7edb4d" + "reference": "1854739267d81d38eae7d8c623caf523f30f256b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/e508e34a502a3acc3329b464dad257378a7edb4d", - "reference": "e508e34a502a3acc3329b464dad257378a7edb4d", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/1854739267d81d38eae7d8c623caf523f30f256b", + "reference": "1854739267d81d38eae7d8c623caf523f30f256b", "shasum": "" }, "require": { "composer/semver": "^3.3", "ext-json": "*", - "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0", + "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0||^13.0", "php": "^7.0||^8.0", "phpoffice/phpspreadsheet": "^1.30.0", "psr/simple-cache": "^1.0||^2.0||^3.0" }, "require-dev": { - "laravel/scout": "^7.0||^8.0||^9.0||^10.0", - "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0||^10.0", + "laravel/scout": "^7.0||^8.0||^9.0||^10.0||^11.0", + "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0||^10.0||^11.0", "predis/predis": "^1.1" }, "type": "library", @@ -3726,7 +3930,7 @@ ], "support": { "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", - "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.67" + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.68" }, "funding": [ { @@ -3738,20 +3942,20 @@ "type": "github" } ], - "time": "2025-08-26T09:13:16+00:00" + "time": "2026-03-17T20:51:10+00:00" }, { "name": "maennchen/zipstream-php", - "version": "3.2.0", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416" + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/9712d8fa4cdf9240380b01eb4be55ad8dcf71416", - "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", "shasum": "" }, "require": { @@ -3762,7 +3966,7 @@ "require-dev": { "brianium/paratest": "^7.7", "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.16", + "friendsofphp/php-cs-fixer": "^3.86", "guzzlehttp/guzzle": "^7.5", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.5", @@ -3808,7 +4012,7 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.0" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2" }, "funding": [ { @@ -3816,7 +4020,7 @@ "type": "github" } ], - "time": "2025-07-17T11:15:13+00:00" + "time": "2026-04-11T18:38:28+00:00" }, { "name": "markbaker/complex", @@ -3994,42 +4198,43 @@ }, { "name": "monolog/monolog", - "version": "2.10.0", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "5cf826f2991858b54d5c3809bee745560a1042a7" + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5cf826f2991858b54d5c3809bee745560a1042a7", - "reference": "5cf826f2991858b54d5c3809bee745560a1042a7", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", "shasum": "" }, "require": { - "php": ">=7.2", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" }, "provide": { - "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + "psr/log-implementation": "3.0.0" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "aws/aws-sdk-php": "^3.0", "doctrine/couchdb": "~1.0@dev", "elasticsearch/elasticsearch": "^7 || ^8", "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2@dev", - "guzzlehttp/guzzle": "^7.4", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", + "mongodb/mongodb": "^1.8 || ^2.0", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpspec/prophecy": "^1.15", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.38 || ^9.6.19", - "predis/predis": "^1.1 || ^2.0", - "rollbar/rollbar": "^1.3 || ^2 || ^3", - "ruflin/elastica": "^7", - "swiftmailer/swiftmailer": "^5.3|^6.0", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -4052,7 +4257,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -4080,7 +4285,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.10.0" + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" }, "funding": [ { @@ -4092,7 +4297,7 @@ "type": "tidelift" } ], - "time": "2024-11-12T12:43:37+00:00" + "time": "2026-01-02T08:56:05+00:00" }, { "name": "nesbot/carbon", @@ -4203,25 +4408,27 @@ }, { "name": "nette/schema", - "version": "v1.3.2", + "version": "v1.3.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.4" + "php": "8.1 - 8.5" }, "require-dev": { - "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^1.0", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -4231,6 +4438,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -4259,26 +4469,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.2" + "source": "https://github.com/nette/schema/tree/v1.3.5" }, - "time": "2024-10-06T23:10:23+00:00" + "time": "2026-02-23T03:47:12+00:00" }, { "name": "nette/utils", - "version": "v4.0.8", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", "shasum": "" }, "require": { - "php": "8.0 - 8.5" + "php": "8.2 - 8.5" }, "conflict": { "nette/finder": "<3", @@ -4286,8 +4496,10 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", "nette/tester": "^2.5", - "phpstan/phpstan-nette": "^2.0@stable", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -4301,7 +4513,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -4348,22 +4560,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.8" + "source": "https://github.com/nette/utils/tree/v4.1.3" }, - "time": "2025-08-06T21:43:34+00:00" + "time": "2026-02-13T03:05:33+00:00" }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -4406,9 +4618,94 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/5369ef84d8142c1d87e4ec278711d4ece3cbf301", + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.4.15" + }, + "require-dev": { + "illuminate/console": "^10.48.24", + "illuminate/support": "^10.48.24", + "laravel/pint": "^1.18.2", + "pestphp/pest": "^2.36.0", + "pestphp/pest-plugin-mock": "2.0.0", + "phpstan/phpstan": "^1.12.11", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^6.4.15", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v1.17.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-11-21T10:36:35+00:00" }, { "name": "nyholm/psr7", @@ -4488,91 +4785,28 @@ ], "time": "2024-09-09T07:06:30+00:00" }, - { - "name": "opis/closure", - "version": "3.7.0", - "source": { - "type": "git", - "url": "https://github.com/opis/closure.git", - "reference": "b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4", - "reference": "b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0" - }, - "require-dev": { - "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.6.x-dev" - } - }, - "autoload": { - "files": [ - "functions.php" - ], - "psr-4": { - "Opis\\Closure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marius Sarca", - "email": "marius.sarca@gmail.com" - }, - { - "name": "Sorin Sarca", - "email": "sarca_sorin@hotmail.com" - } - ], - "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", - "homepage": "https://opis.io/closure", - "keywords": [ - "anonymous functions", - "closure", - "function", - "serializable", - "serialization", - "serialize" - ], - "support": { - "issues": "https://github.com/opis/closure/issues", - "source": "https://github.com/opis/closure/tree/3.7.0" - }, - "time": "2025-07-08T20:30:08+00:00" - }, { "name": "paragonie/constant_time_encoding", - "version": "v3.0.0", + "version": "v3.1.3", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", "shasum": "" }, "require": { "php": "^8" }, "require-dev": { - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4|^5" + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" }, "type": "library", "autoload": { @@ -4618,7 +4852,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:36:18+00:00" + "time": "2025-09-24T15:06:41+00:00" }, { "name": "paragonie/random_compat", @@ -4762,16 +4996,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "1.30.0", + "version": "1.30.3", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "2f39286e0136673778b7a142b3f0d141e43d1714" + "reference": "d28d4827f934469e7ca4de940ab0abd0788d1e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2f39286e0136673778b7a142b3f0d141e43d1714", - "reference": "2f39286e0136673778b7a142b3f0d141e43d1714", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/d28d4827f934469e7ca4de940ab0abd0788d1e65", + "reference": "d28d4827f934469e7ca4de940ab0abd0788d1e65", "shasum": "" }, "require": { @@ -4793,13 +5027,12 @@ "maennchen/zipstream-php": "^2.1 || ^3.0", "markbaker/complex": "^3.0", "markbaker/matrix": "^3.0", - "php": "^7.4 || ^8.0", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", + "php": ">=7.4.0 <8.5.0", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "doctrine/instantiator": "^1.5", "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0", "friendsofphp/php-cs-fixer": "^3.2", "mitoteam/jpgraph": "^10.3", @@ -4846,6 +5079,9 @@ }, { "name": "Adrien Crivelli" + }, + { + "name": "Owen Leibman" } ], "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", @@ -4862,22 +5098,22 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.3" }, - "time": "2025-08-10T06:28:02+00:00" + "time": "2026-04-10T03:47:16+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.4", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", "shasum": "" }, "require": { @@ -4927,7 +5163,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" }, "funding": [ { @@ -4939,20 +5175,20 @@ "type": "tidelift" } ], - "time": "2025-08-21T11:53:16+00:00" + "time": "2025-12-27T19:41:33+00:00" }, { "name": "phpseclib/phpseclib", - "version": "3.0.46", + "version": "3.0.51", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + "reference": "d59c94077f9c9915abb51ddb52ce85188ece1748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d59c94077f9c9915abb51ddb52ce85188ece1748", + "reference": "d59c94077f9c9915abb51ddb52ce85188ece1748", "shasum": "" }, "require": { @@ -5033,7 +5269,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.51" }, "funding": [ { @@ -5049,7 +5285,7 @@ "type": "tidelift" } ], - "time": "2025-06-26T16:29:55+00:00" + "time": "2026-04-10T01:33:53+00:00" }, { "name": "pragmarx/google2fa", @@ -5105,27 +5341,27 @@ }, { "name": "pragmarx/google2fa-laravel", - "version": "v2.3.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa-laravel.git", - "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef" + "reference": "d885bb5bca8be03b226d040aa80250402760a67c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/60f363c16db1e94263e0560efebe9fc2e302b7ef", - "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/d885bb5bca8be03b226d040aa80250402760a67c", + "reference": "d885bb5bca8be03b226d040aa80250402760a67c", "shasum": "" }, "require": { - "laravel/framework": "^5.4.36|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "laravel/framework": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": ">=7.0", "pragmarx/google2fa-qrcode": "^1.0|^2.0|^3.0" }, "require-dev": { "bacon/bacon-qr-code": "^2.0", - "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*|9.*|10.*", - "phpunit/phpunit": "~5|~6|~7|~8|~9|~10|~11" + "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*|9.*|10.*|11.*", + "phpunit/phpunit": "~5|~6|~7|~8|~9|~10|~11|~12" }, "suggest": { "bacon/bacon-qr-code": "Required to generate inline QR Codes.", @@ -5175,27 +5411,27 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa-laravel/issues", - "source": "https://github.com/antonioribeiro/google2fa-laravel/tree/v2.3.0" + "source": "https://github.com/antonioribeiro/google2fa-laravel/tree/v3.0.1" }, - "time": "2025-02-26T19:39:35+00:00" + "time": "2026-03-17T20:54:53+00:00" }, { "name": "pragmarx/google2fa-qrcode", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", - "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b" + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/ce4d8a729b6c93741c607cfb2217acfffb5bf76b", - "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/c23ebcc3a50de0d1566016a6dd1486e183bb78e1", + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1", "shasum": "" }, "require": { "php": ">=7.1", - "pragmarx/google2fa": ">=4.0" + "pragmarx/google2fa": "^4.0|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "bacon/bacon-qr-code": "^2.0", @@ -5242,9 +5478,9 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", - "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.0" + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.1" }, - "time": "2021-08-15T12:53:48+00:00" + "time": "2025-09-19T23:02:26+00:00" }, { "name": "psr/cache", @@ -5345,22 +5581,27 @@ }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "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/" @@ -5387,9 +5628,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/event-dispatcher", @@ -5603,16 +5844,16 @@ }, { "name": "psr/log", - "version": "2.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", - "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -5621,7 +5862,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { @@ -5647,31 +5888,31 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/2.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:41:46+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -5686,7 +5927,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -5698,22 +5939,22 @@ "simple-cache" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/master" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "time": "2017-10-23T01:57:42+00:00" + "time": "2021-10-29T13:26:27+00:00" }, { "name": "psy/psysh", - "version": "v0.12.10", + "version": "v0.12.22", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22" + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6e80abe6f2257121f1eb9a4c55bf29d921025b22", - "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3be75d5b9244936dd4ac62ade2bfb004d13acf0f", + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f", "shasum": "" }, "require": { @@ -5721,18 +5962,19 @@ "ext-tokenizer": "*", "nikic/php-parser": "^5.0 || ^4.0", "php": "^8.0 || ^7.4", - "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" }, "conflict": { "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" }, "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." }, "bin": [ @@ -5776,9 +6018,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.10" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.22" }, - "time": "2025-08-04T12:39:37+00:00" + "time": "2026-03-22T23:03:24+00:00" }, { "name": "ralouphie/getallheaders", @@ -5902,20 +6144,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "8429c78ca35a09f27565311b98101e2826affde0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -5974,9 +6216,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.2" }, - "time": "2025-06-25T14:20:11+00:00" + "time": "2025-12-14T04:43:48+00:00" }, { "name": "reliese/laravel", @@ -6205,27 +6447,27 @@ }, { "name": "setasign/fpdi", - "version": "v2.6.4", + "version": "v2.6.6", "source": { "type": "git", "url": "https://github.com/Setasign/FPDI.git", - "reference": "4b53852fde2734ec6a07e458a085db627c60eada" + "reference": "de0cf35911be3e9ea63b48e0f307883b1c7c48ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/4b53852fde2734ec6a07e458a085db627c60eada", - "reference": "4b53852fde2734ec6a07e458a085db627c60eada", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/de0cf35911be3e9ea63b48e0f307883b1c7c48ac", + "reference": "de0cf35911be3e9ea63b48e0f307883b1c7c48ac", "shasum": "" }, "require": { "ext-zlib": "*", - "php": "^7.1 || ^8.0" + "php": ">=7.2 <=8.5.99999" }, "conflict": { "setasign/tfpdf": "<1.31" }, "require-dev": { - "phpunit/phpunit": "^7", + "phpunit/phpunit": "^8.5.52", "setasign/fpdf": "~1.8.6", "setasign/tfpdf": "~1.33", "squizlabs/php_codesniffer": "^3.5", @@ -6265,7 +6507,7 @@ ], "support": { "issues": "https://github.com/Setasign/FPDI/issues", - "source": "https://github.com/Setasign/FPDI/tree/v2.6.4" + "source": "https://github.com/Setasign/FPDI/tree/v2.6.6" }, "funding": [ { @@ -6273,46 +6515,37 @@ "type": "tidelift" } ], - "time": "2025-08-05T09:57:14+00:00" + "time": "2026-03-13T08:38:20+00:00" }, { - "name": "swiftmailer/swiftmailer", - "version": "v6.3.0", + "name": "spatie/once", + "version": "3.1.2", "source": { "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c" + "url": "https://github.com/spatie/once.git", + "reference": "54c9908aff00913e82422da6938fba7bd2bf6c08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8a5d5072dca8f48460fce2f4131fcc495eec654c", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c", + "url": "https://api.github.com/repos/spatie/once/zipball/54c9908aff00913e82422da6938fba7bd2bf6c08", + "reference": "54c9908aff00913e82422da6938fba7bd2bf6c08", "shasum": "" }, "require": { - "egulias/email-validator": "^2.0|^3.1", - "php": ">=7.0.0", - "symfony/polyfill-iconv": "^1.0", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" + "php": "^8.0" }, "require-dev": { - "mockery/mockery": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.4" - }, - "suggest": { - "ext-intl": "Needed to support internationalized email addresses" + "pestphp/pest": "^1.21", + "symfony/var-dumper": "^5.1|^6.0|^7.0|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.2-dev" - } - }, "autoload": { "files": [ - "lib/swift_required.php" - ] + "src/functions.php" + ], + "psr-4": { + "Spatie\\Once\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6320,85 +6553,75 @@ ], "authors": [ { - "name": "Chris Corbyn" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", + "description": "A magic memoization function", + "homepage": "https://github.com/spatie/once", "keywords": [ - "email", - "mail", - "mailer" + "cache", + "callable", + "memoization", + "once", + "spatie" ], "support": { - "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.3.0" + "source": "https://github.com/spatie/once/tree/3.1.2" }, "funding": [ { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer", - "type": "tidelift" + "url": "https://spatie.be/open-source/support-us", + "type": "custom" } ], - "abandoned": "symfony/mailer", - "time": "2021-10-18T15:26:12+00:00" + "time": "2025-11-25T15:44:06+00:00" }, { "name": "symfony/console", - "version": "v5.4.47", + "version": "v6.4.36", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" + "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "url": "https://api.github.com/repos/symfony/console/zipball/9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", + "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6432,7 +6655,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.47" + "source": "https://github.com/symfony/console/tree/v6.4.36" }, "funding": [ { @@ -6443,25 +6666,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-11-06T11:30:55+00:00" + "time": "2026-03-27T15:30:51+00:00" }, { "name": "symfony/css-selector", - "version": "v7.3.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "reference": "b055f228a4178a1d6774909903905e3475f3eac8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/b055f228a4178a1d6774909903905e3475f3eac8", + "reference": "b055f228a4178a1d6774909903905e3475f3eac8", "shasum": "" }, "require": { @@ -6497,7 +6724,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + "source": "https://github.com/symfony/css-selector/tree/v7.4.8" }, "funding": [ { @@ -6508,12 +6735,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": "2024-09-25T14:21:43+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6584,27 +6815,31 @@ }, { "name": "symfony/error-handler", - "version": "v5.4.46", + "version": "v6.4.36", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "d19ede7a2cafb386be9486c580649d0f9e3d0363" + "reference": "2ea68f0e1835ad6a126f93bbc14cd236c10ab361" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/d19ede7a2cafb386be9486c580649d0f9e3d0363", - "reference": "d19ede7a2cafb386be9486c580649d0f9e3d0363", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/2ea68f0e1835ad6a126f93bbc14cd236c10ab361", + "reference": "2ea68f0e1835ad6a126f93bbc14cd236c10ab361", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -6635,7 +6870,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/v5.4.46" + "source": "https://github.com/symfony/error-handler/tree/v6.4.36" }, "funding": [ { @@ -6646,33 +6881,37 @@ "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-11-05T14:17:06+00:00" + "time": "2026-03-10T15:56:14+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.4.25", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b0cf3162020603587363f0551cd3be43958611ff" + "reference": "f57b899fa736fd71121168ef268f23c206083f0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b0cf3162020603587363f0551cd3be43958611ff", - "reference": "b0cf3162020603587363f0551cd3be43958611ff", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f57b899fa736fd71121168ef268f23c206083f0a", + "reference": "f57b899fa736fd71121168ef268f23c206083f0a", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4", + "symfony/dependency-injection": "<6.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -6681,13 +6920,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6715,7 +6955,7 @@ "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/v6.4.25" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.8" }, "funding": [ { @@ -6735,7 +6975,7 @@ "type": "tidelift" } ], - "time": "2025-08-13T09:41:44+00:00" + "time": "2026-03-30T13:54:39+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6815,22 +7055,23 @@ }, { "name": "symfony/finder", - "version": "v5.4.45", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "63741784cd7b9967975eec610b256eed3ede022b" + "reference": "9590e86be1d1c57bfbb16d0dd040345378c20896" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b", - "reference": "63741784cd7b9967975eec610b256eed3ede022b", + "url": "https://api.github.com/repos/symfony/finder/zipball/9590e86be1d1c57bfbb16d0dd040345378c20896", + "reference": "9590e86be1d1c57bfbb16d0dd040345378c20896", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -6858,7 +7099,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.45" + "source": "https://github.com/symfony/finder/tree/v6.4.34" }, "funding": [ { @@ -6869,44 +7110,49 @@ "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-28T13:32:08+00:00" + "time": "2026-01-28T15:16:37+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.4.48", + "version": "v6.4.35", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "3f38b8af283b830e1363acd79e5bc3412d055341" + "reference": "cffffd0a2c037117b742b4f8b379a22a2a33f6d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f38b8af283b830e1363acd79e5bc3412d055341", - "reference": "3f38b8af283b830e1363acd79e5bc3412d055341", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cffffd0a2c037117b742b4f8b379a22a2a33f6d2", + "reference": "cffffd0a2c037117b742b4f8b379a22a2a33f6d2", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" }, "require-dev": { - "predis/predis": "^1.0|^2.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" - }, - "suggest": { - "symfony/mime": "To use the file extension guesser" + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6934,7 +7180,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.48" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.35" }, "funding": [ { @@ -6945,82 +7191,87 @@ "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-11-13T18:58:02+00:00" + "time": "2026-03-06T11:15:58+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.48", + "version": "v6.4.36", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "c2dbfc92b851404567160d1ecf3fb7d9b7bde9b0" + "reference": "4087ec02119de450e9ebb60806d69c6bb8c6e468" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c2dbfc92b851404567160d1ecf3fb7d9b7bde9b0", - "reference": "c2dbfc92b851404567160d1ecf3fb7d9b7bde9b0", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4087ec02119de450e9ebb60806d69c6bb8c6e468", + "reference": "4087ec02119de450e9ebb60806d69c6bb8c6e468", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^5.0|^6.0", - "symfony/http-foundation": "^5.4.21|^6.2.7", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.0", - "symfony/config": "<5.0", - "symfony/console": "<4.4", - "symfony/dependency-injection": "<5.3", - "symfony/doctrine-bridge": "<5.0", - "symfony/form": "<5.0", - "symfony/http-client": "<5.0", - "symfony/mailer": "<5.0", - "symfony/messenger": "<5.0", - "symfony/translation": "<5.0", - "symfony/twig-bridge": "<5.0", - "symfony/validator": "<5.0", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.3", "twig/twig": "<2.13" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/config": "^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", - "symfony/var-dumper": "^4.4.31|^5.4", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" - }, "type": "library", "autoload": { "psr-4": { @@ -7047,7 +7298,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/v5.4.48" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.36" }, "funding": [ { @@ -7058,49 +7309,137 @@ "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-11-27T12:43:17+00:00" + "time": "2026-03-31T20:38:11+00:00" }, { - "name": "symfony/mime", - "version": "v5.4.45", + "name": "symfony/mailer", + "version": "v6.4.34", "source": { "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "8c1b9b3e5b52981551fc6044539af1d974e39064" + "url": "https://github.com/symfony/mailer.git", + "reference": "01b846f48e53ee4096692a383637a1fa4d577301" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/8c1b9b3e5b52981551fc6044539af1d974e39064", - "reference": "8c1b9b3e5b52981551fc6044539af1d974e39064", + "url": "https://api.github.com/repos/symfony/mailer/zipball/01b846f48e53ee4096692a383637a1fa4d577301", + "reference": "01b846f48e53ee4096692a383637a1fa4d577301", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "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": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.4.34" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "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": "2026-02-24T09:34:36+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.4.36", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "9c31726137c70798f815fb98293ffb8a2a47694c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/9c31726137c70798f815fb98293ffb8a2a47694c", + "reference": "9c31726137c70798f815fb98293ffb8a2a47694c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16" + "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": "<4.4", - "symfony/serializer": "<5.4.35|>=6,<6.3.12|>=6.4,<6.4.3" + "symfony/mailer": "<5.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": "^4.4|^5.0|^6.0", - "symfony/process": "^5.4|^6.4", - "symfony/property-access": "^4.4|^5.1|^6.0", - "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/serializer": "^5.4.35|~6.3.12|^6.4.3" + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" }, "type": "library", "autoload": { @@ -7132,7 +7471,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.4.45" + "source": "https://github.com/symfony/mime/tree/v6.4.36" }, "funding": [ { @@ -7143,25 +7482,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-10-23T20:18:32+00:00" + "time": "2026-03-30T09:31:23+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -7211,7 +7554,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.35.0" }, "funding": [ { @@ -7231,104 +7574,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-iconv", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "5f3b930437ae03ae5dff61269024d8ea1b3774aa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/5f3b930437ae03ae5dff61269024d8ea1b3774aa", - "reference": "5f3b930437ae03ae5dff61269024d8ea1b3774aa", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-iconv": "*" - }, - "suggest": { - "ext-iconv": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" - } - }, - "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 Iconv extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "iconv", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "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-17T14:58:18+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/ad1b7b9092976d6c948b8a187cec9faaea9ec1df", + "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df", "shasum": "" }, "require": { @@ -7377,7 +7636,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.35.0" }, "funding": [ { @@ -7397,11 +7656,11 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -7464,7 +7723,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.35.0" }, "funding": [ { @@ -7488,7 +7747,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -7549,7 +7808,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.35.0" }, "funding": [ { @@ -7573,16 +7832,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { @@ -7634,7 +7893,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.35.0" }, "funding": [ { @@ -7654,100 +7913,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", - "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\\Php73\\": "" - }, - "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 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "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": "2026-04-10T17:25:58+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -7798,7 +7977,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.35.0" }, "funding": [ { @@ -7818,25 +7997,187 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "symfony/process", - "version": "v5.4.47", + "name": "symfony/polyfill-php83", + "version": "v1.35.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149", + "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "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.35.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "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": "2026-04-10T17:25:58+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.35.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.35.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "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": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/process", + "version": "v6.4.33", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "c46e854e79b52d07666e43924a20cb6dc546644e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/c46e854e79b52d07666e43924a20cb6dc546644e", + "reference": "c46e854e79b52d07666e43924a20cb6dc546644e", + "shasum": "" + }, + "require": { + "php": ">=8.1" }, "type": "library", "autoload": { @@ -7864,7 +8205,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.47" + "source": "https://github.com/symfony/process/tree/v6.4.33" }, "funding": [ { @@ -7875,12 +8216,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": "2024-11-06T11:36:42+00:00" + "time": "2026-01-23T16:02:12+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -7973,43 +8318,36 @@ }, { "name": "symfony/routing", - "version": "v5.4.48", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "dd08c19879a9b37ff14fd30dcbdf99a4cf045db1" + "reference": "5ab3a3e1a03535ec5ca6ce2d39e4369a1096ae47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/dd08c19879a9b37ff14fd30dcbdf99a4cf045db1", - "reference": "dd08c19879a9b37ff14fd30dcbdf99a4cf045db1", + "url": "https://api.github.com/repos/symfony/routing/zipball/5ab3a3e1a03535ec5ca6ce2d39e4369a1096ae47", + "reference": "5ab3a3e1a03535ec5ca6ce2d39e4369a1096ae47", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", - "symfony/config": "<5.3", - "symfony/dependency-injection": "<4.4", - "symfony/yaml": "<4.4" + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" }, "require-dev": { "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -8043,7 +8381,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.48" + "source": "https://github.com/symfony/routing/tree/v6.4.34" }, "funding": [ { @@ -8054,25 +8392,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-11-12T18:20:21+00:00" + "time": "2026-02-24T17:34:50+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -8126,7 +8468,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -8137,31 +8479,36 @@ "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-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v6.4.25", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "7cdec7edfaf2cdd9c18901e35bcf9653d6209ff1" + "reference": "114ac57257d75df748eda23dd003878080b8e688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/7cdec7edfaf2cdd9c18901e35bcf9653d6209ff1", - "reference": "7cdec7edfaf2cdd9c18901e35bcf9653d6209ff1", + "url": "https://api.github.com/repos/symfony/string/zipball/114ac57257d75df748eda23dd003878080b8e688", + "reference": "114ac57257d75df748eda23dd003878080b8e688", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.33", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, @@ -8169,11 +8516,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -8212,7 +8559,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.25" + "source": "https://github.com/symfony/string/tree/v7.4.8" }, "funding": [ { @@ -8232,20 +8579,20 @@ "type": "tidelift" } ], - "time": "2025-08-22T12:33:20+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/translation", - "version": "v6.4.24", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1" + "reference": "d07d117db41341511671b0a1a2be48f2772189ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/300b72643e89de0734d99a9e3f8494a3ef6936e1", - "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1", + "url": "https://api.github.com/repos/symfony/translation/zipball/d07d117db41341511671b0a1a2be48f2772189ce", + "reference": "d07d117db41341511671b0a1a2be48f2772189ce", "shasum": "" }, "require": { @@ -8311,7 +8658,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.24" + "source": "https://github.com/symfony/translation/tree/v6.4.34" }, "funding": [ { @@ -8331,20 +8678,20 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:30:48+00:00" + "time": "2026-02-16T20:44:03+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, "require": { @@ -8393,7 +8740,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, "funding": [ { @@ -8404,47 +8751,124 @@ "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-27T08:32:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { - "name": "symfony/var-dumper", - "version": "v5.4.48", + "name": "symfony/uid", + "version": "v6.4.32", "source": { "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "42f18f170aa86d612c3559cfb3bd11a375df32c8" + "url": "https://github.com/symfony/uid.git", + "reference": "6b973c385f00341b246f697d82dc01a09107acdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/42f18f170aa86d612c3559cfb3bd11a375df32c8", - "reference": "42f18f170aa86d612c3559cfb3bd11a375df32c8", + "url": "https://api.github.com/repos/symfony/uid/zipball/6b973c385f00341b246f697d82dc01a09107acdd", + "reference": "6b973c385f00341b246f697d82dc01a09107acdd", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/console": "<4.4" + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "twig/twig": "^2.13|^3.0.4" + "symfony/console": "^5.4|^6.0|^7.0" }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v6.4.32" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "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-12-23T15:07:59+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.4.36", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7c8ad9ce4faf6c8a99948e70ce02b601a0439782", + "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4" }, "bin": [ "Resources/bin/var-dump-server" @@ -8482,7 +8906,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.48" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.36" }, "funding": [ { @@ -8493,32 +8917,36 @@ "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-11-08T15:21:10+00:00" + "time": "2026-03-30T15:36:00+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^7.4 || ^8.0", - "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^2.0", @@ -8551,32 +8979,32 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" }, - "time": "2024-12-21T16:25:41+00:00" + "time": "2025-12-02T11:56:42+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.6.2", + "version": "v5.6.3", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + "reference": "955e7815d677a3eaa7075231212f2110983adecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.3", + "graham-campbell/result-type": "^1.1.4", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3", - "symfony/polyfill-ctype": "^1.24", - "symfony/polyfill-mbstring": "^1.24", - "symfony/polyfill-php80": "^1.24" + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", @@ -8625,7 +9053,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" }, "funding": [ { @@ -8637,20 +9065,20 @@ "type": "tidelift" } ], - "time": "2025-04-30T23:37:27+00:00" + "time": "2025-12-27T19:49:13+00:00" }, { "name": "voku/portable-ascii", - "version": "1.6.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a" + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/87337c91b9dfacee02452244ee14ab3c43bc485a", - "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { @@ -8675,7 +9103,7 @@ "authors": [ { "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", @@ -8687,7 +9115,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/1.6.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -8711,94 +9139,42 @@ "type": "tidelift" } ], - "time": "2022-01-24T18:55:24+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "yajra/laravel-datatables-oracle", - "version": "v9.21.2", + "version": "v10.11.4", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables.git", - "reference": "a7fd01f06282923e9c63fa27fe6b391e21dc321a" + "reference": "a11dc9cf5ec7f7dc68ad0d5959c5e7aec0af29a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables/zipball/a7fd01f06282923e9c63fa27fe6b391e21dc321a", - "reference": "a7fd01f06282923e9c63fa27fe6b391e21dc321a", + "url": "https://api.github.com/repos/yajra/laravel-datatables/zipball/a11dc9cf5ec7f7dc68ad0d5959c5e7aec0af29a5", + "reference": "a11dc9cf5ec7f7dc68ad0d5959c5e7aec0af29a5", "shasum": "" }, "require": { - "illuminate/database": "5.8.*|^6|^7|^8|^9", - "illuminate/filesystem": "5.8.*|^6|^7|^8|^9", - "illuminate/http": "5.8.*|^6|^7|^8|^9", - "illuminate/support": "5.8.*|^6|^7|^8|^9", - "illuminate/view": "5.8.*|^6|^7|^8|^9", - "php": "^7.1.3|^8" + "illuminate/database": "^9|^10", + "illuminate/filesystem": "^9|^10", + "illuminate/http": "^9|^10", + "illuminate/support": "^9|^10", + "illuminate/view": "^9|^10", + "php": "^8.0.2" }, "require-dev": { - "orchestra/testbench": "^3.8|^4.0|^5.0|^6.0|^7.0" + "algolia/algoliasearch-client-php": "^3.4", + "larastan/larastan": "^2.4", + "laravel/scout": "^10.5", + "meilisearch/meilisearch-php": "^1.4", + "orchestra/testbench": "^8", + "yajra/laravel-datatables-html": "^9.3.4|^10" }, "suggest": { "yajra/laravel-datatables-buttons": "Plugin for server-side exporting of dataTables.", "yajra/laravel-datatables-editor": "Plugin to use DataTables Editor (requires a license).", + "yajra/laravel-datatables-export": "Plugin for server-side exporting using livewire and queue worker.", "yajra/laravel-datatables-fractal": "Plugin for server-side response using Fractal.", "yajra/laravel-datatables-html": "Plugin for server-side HTML builder of dataTables." }, @@ -8813,7 +9189,7 @@ ] }, "branch-alias": { - "dev-master": "9.0-dev" + "dev-master": "10.x-dev" } }, "autoload": { @@ -8834,7 +9210,7 @@ "email": "aqangeles@gmail.com" } ], - "description": "jQuery DataTables API for Laravel 5|6|7|8|9", + "description": "jQuery DataTables API for Laravel 4|5|6|7|8|9|10", "keywords": [ "datatables", "jquery", @@ -8842,48 +9218,44 @@ ], "support": { "issues": "https://github.com/yajra/laravel-datatables/issues", - "source": "https://github.com/yajra/laravel-datatables/tree/v9.21.2" + "source": "https://github.com/yajra/laravel-datatables/tree/v10.11.4" }, "funding": [ { - "url": "https://www.paypal.me/yajra", - "type": "custom" - }, - { - "url": "https://www.patreon.com/yajra", - "type": "patreon" + "url": "https://github.com/sponsors/yajra", + "type": "github" } ], - "time": "2022-07-12T04:48:03+00:00" + "time": "2024-02-28T05:00:23+00:00" } ], "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v3.7.0", + "version": "v3.16.5", "source": { "type": "git", - "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "3372ed65e6d2039d663ed19aa699956f9d346271" + "url": "https://github.com/fruitcake/laravel-debugbar.git", + "reference": "e85c0a8464da67e5b4a53a42796d46a43fc06c9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/3372ed65e6d2039d663ed19aa699956f9d346271", - "reference": "3372ed65e6d2039d663ed19aa699956f9d346271", + "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/e85c0a8464da67e5b4a53a42796d46a43fc06c9a", + "reference": "e85c0a8464da67e5b4a53a42796d46a43fc06c9a", "shasum": "" }, "require": { - "illuminate/routing": "^7|^8|^9", - "illuminate/session": "^7|^8|^9", - "illuminate/support": "^7|^8|^9", - "maximebf/debugbar": "^1.17.2", - "php": ">=7.2.5", - "symfony/finder": "^5|^6" + "illuminate/routing": "^10|^11|^12", + "illuminate/session": "^10|^11|^12", + "illuminate/support": "^10|^11|^12", + "php": "^8.1", + "php-debugbar/php-debugbar": "^2.2.4", + "symfony/finder": "^6|^7|^8" }, "require-dev": { "mockery/mockery": "^1.3.3", - "orchestra/testbench-dusk": "^5|^6|^7", - "phpunit/phpunit": "^8.5|^9.0", + "orchestra/testbench-dusk": "^7|^8|^9|^10", + "phpunit/phpunit": "^9.5.10|^10|^11", "squizlabs/php_codesniffer": "^3.5" }, "type": "library", @@ -8897,7 +9269,7 @@ ] }, "branch-alias": { - "dev-master": "3.6-dev" + "dev-master": "3.16-dev" } }, "autoload": { @@ -8922,13 +9294,14 @@ "keywords": [ "debug", "debugbar", + "dev", "laravel", "profiler", "webprofiler" ], "support": { - "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.7.0" + "issues": "https://github.com/fruitcake/laravel-debugbar/issues", + "source": "https://github.com/fruitcake/laravel-debugbar/tree/v3.16.5" }, "funding": [ { @@ -8940,47 +9313,47 @@ "type": "github" } ], - "time": "2022-07-11T09:26:42+00:00" + "time": "2026-01-23T15:03:22+00:00" }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.14.0", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "485c756f6cff408d6b273274c5e86112c3973d98" + "reference": "591e7d665fbab8a3b682e451641706341573eb80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/485c756f6cff408d6b273274c5e86112c3973d98", - "reference": "485c756f6cff408d6b273274c5e86112c3973d98", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/591e7d665fbab8a3b682e451641706341573eb80", + "reference": "591e7d665fbab8a3b682e451641706341573eb80", "shasum": "" }, "require": { - "barryvdh/reflection-docblock": "^2.0.6", + "barryvdh/reflection-docblock": "^2.1.1", "composer/class-map-generator": "^1.0", - "doctrine/dbal": "^2.6 || ^3", "ext-json": "*", - "illuminate/console": "^8 || ^9 || ^10", - "illuminate/filesystem": "^8 || ^9 || ^10", - "illuminate/support": "^8 || ^9 || ^10", + "illuminate/console": "^10 || ^11", + "illuminate/database": "^10.38 || ^11", + "illuminate/filesystem": "^10 || ^11", + "illuminate/support": "^10 || ^11", "nikic/php-parser": "^4.18 || ^5", - "php": "^7.3 || ^8.0", + "php": "^8.1", "phpdocumentor/type-resolver": "^1.1.0" }, "require-dev": { "ext-pdo_sqlite": "*", - "friendsofphp/php-cs-fixer": "^2", - "illuminate/config": "^8 || ^9 || ^10", - "illuminate/view": "^8 || ^9 || ^10", + "friendsofphp/php-cs-fixer": "^3", + "illuminate/config": "^9 || ^10 || ^11", + "illuminate/view": "^9 || ^10 || ^11", "mockery/mockery": "^1.4", - "orchestra/testbench": "^6 || ^7 || ^8", - "phpunit/phpunit": "^8.5 || ^9", - "spatie/phpunit-snapshot-assertions": "^3 || ^4", + "orchestra/testbench": "^8 || ^9", + "phpunit/phpunit": "^10.5", + "spatie/phpunit-snapshot-assertions": "^4 || ^5", "vimeo/psalm": "^5.4" }, "suggest": { - "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10)." + "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)." }, "type": "library", "extra": { @@ -8990,7 +9363,7 @@ ] }, "branch-alias": { - "dev-master": "2.14-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -9022,7 +9395,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", - "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.14.0" + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.1.0" }, "funding": [ { @@ -9034,20 +9407,20 @@ "type": "github" } ], - "time": "2024-02-05T08:16:36+00:00" + "time": "2024-07-12T14:20:51+00:00" }, { "name": "barryvdh/reflection-docblock", - "version": "v2.4.0", + "version": "v2.4.1", "source": { "type": "git", "url": "https://github.com/barryvdh/ReflectionDocBlock.git", - "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201" + "reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/d103774cbe7e94ddee7e4870f97f727b43fe7201", - "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b", + "reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b", "shasum": "" }, "require": { @@ -9084,28 +9457,28 @@ } ], "support": { - "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.0" + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.1" }, - "time": "2025-07-17T06:07:30+00:00" + "time": "2026-03-05T20:09:01+00:00" }, { "name": "composer/class-map-generator", - "version": "1.6.2", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" + "reference": "6a9c2f0970022ab00dc58c07d0685dd712f2231b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/6a9c2f0970022ab00dc58c07d0685dd712f2231b", + "reference": "6a9c2f0970022ab00dc58c07d0685dd712f2231b", "shasum": "" }, "require": { "composer/pcre": "^2.1 || ^3.1", "php": "^7.2 || ^8.0", - "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" }, "require-dev": { "phpstan/phpstan": "^1.12 || ^2", @@ -9113,7 +9486,7 @@ "phpstan/phpstan-phpunit": "^1 || ^2", "phpstan/phpstan-strict-rules": "^1.1 || ^2", "phpunit/phpunit": "^8", - "symfony/filesystem": "^5.4 || ^6" + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" }, "type": "library", "extra": { @@ -9143,7 +9516,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.2" + "source": "https://github.com/composer/class-map-generator/tree/1.7.2" }, "funding": [ { @@ -9155,273 +9528,7 @@ "type": "github" } ], - "time": "2025-08-20T18:52:43+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" - }, - "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%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:23:10+00:00" - }, - { - "name": "facade/flare-client-php", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/facade/flare-client-php.git", - "reference": "213fa2c69e120bca4c51ba3e82ed1834ef3f41b8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facade/flare-client-php/zipball/213fa2c69e120bca4c51ba3e82ed1834ef3f41b8", - "reference": "213fa2c69e120bca4c51ba3e82ed1834ef3f41b8", - "shasum": "" - }, - "require": { - "facade/ignition-contracts": "~1.0", - "illuminate/pipeline": "^5.5|^6.0|^7.0|^8.0", - "php": "^7.1|^8.0", - "symfony/http-foundation": "^3.3|^4.1|^5.0", - "symfony/mime": "^3.4|^4.0|^5.1", - "symfony/var-dumper": "^3.4|^4.0|^5.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.14", - "phpunit/phpunit": "^7.5", - "spatie/phpunit-snapshot-assertions": "^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Facade\\FlareClient\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Send PHP errors to Flare", - "homepage": "https://github.com/facade/flare-client-php", - "keywords": [ - "exception", - "facade", - "flare", - "reporting" - ], - "support": { - "issues": "https://github.com/facade/flare-client-php/issues", - "source": "https://github.com/facade/flare-client-php/tree/1.10.0" - }, - "funding": [ - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "time": "2022-08-09T11:23:57+00:00" - }, - { - "name": "facade/ignition", - "version": "2.17.7", - "source": { - "type": "git", - "url": "https://github.com/facade/ignition.git", - "reference": "b4f5955825bb4b74cba0f94001761c46335c33e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facade/ignition/zipball/b4f5955825bb4b74cba0f94001761c46335c33e9", - "reference": "b4f5955825bb4b74cba0f94001761c46335c33e9", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "facade/flare-client-php": "^1.9.1", - "facade/ignition-contracts": "^1.0.2", - "illuminate/support": "^7.0|^8.0", - "monolog/monolog": "^2.0", - "php": "^7.2.5|^8.0", - "symfony/console": "^5.0", - "symfony/var-dumper": "^5.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.14", - "livewire/livewire": "^2.4", - "mockery/mockery": "^1.3", - "orchestra/testbench": "^5.0|^6.0", - "psalm/plugin-laravel": "^1.2" - }, - "suggest": { - "laravel/telescope": "^3.1" - }, - "type": "library", - "extra": { - "laravel": { - "aliases": { - "Flare": "Facade\\Ignition\\Facades\\Flare" - }, - "providers": [ - "Facade\\Ignition\\IgnitionServiceProvider" - ] - }, - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Facade\\Ignition\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A beautiful error page for Laravel applications.", - "homepage": "https://github.com/facade/ignition", - "keywords": [ - "error", - "flare", - "laravel", - "page" - ], - "support": { - "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", - "forum": "https://twitter.com/flareappio", - "issues": "https://github.com/facade/ignition/issues", - "source": "https://github.com/facade/ignition" - }, - "time": "2023-01-26T12:34:59+00:00" - }, - { - "name": "facade/ignition-contracts", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/facade/ignition-contracts.git", - "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", - "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", - "shasum": "" - }, - "require": { - "php": "^7.3|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^v2.15.8", - "phpunit/phpunit": "^9.3.11", - "vimeo/psalm": "^3.17.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Facade\\IgnitionContracts\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://flareapp.io", - "role": "Developer" - } - ], - "description": "Solution contracts for Ignition", - "homepage": "https://github.com/facade/ignition-contracts", - "keywords": [ - "contracts", - "flare", - "ignition" - ], - "support": { - "issues": "https://github.com/facade/ignition-contracts/issues", - "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" - }, - "time": "2020-10-16T08:27:54+00:00" + "time": "2026-03-30T15:36:56+00:00" }, { "name": "fakerphp/faker", @@ -9609,29 +9716,277 @@ "time": "2025-04-30T06:54:44+00:00" }, { - "name": "laravel/sail", - "version": "v1.25.0", + "name": "illuminate/json-schema", + "version": "v12.56.0", "source": { "type": "git", - "url": "https://github.com/laravel/sail.git", - "reference": "e81a7bd7ac1a745ccb25572830fecf74a89bb48a" + "url": "https://github.com/illuminate/json-schema.git", + "reference": "207509531ee53b2c5b966c51c9c63355f8e96e1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/e81a7bd7ac1a745ccb25572830fecf74a89bb48a", - "reference": "e81a7bd7ac1a745ccb25572830fecf74a89bb48a", + "url": "https://api.github.com/repos/illuminate/json-schema/zipball/207509531ee53b2c5b966c51c9c63355f8e96e1e", + "reference": "207509531ee53b2c5b966c51c9c63355f8e96e1e", "shasum": "" }, "require": { - "illuminate/console": "^8.0|^9.0|^10.0", - "illuminate/contracts": "^8.0|^9.0|^10.0", - "illuminate/support": "^8.0|^9.0|^10.0", - "php": "^8.0", - "symfony/yaml": "^6.0" + "illuminate/contracts": "^10.50.0|^11.47.0|^12.40.2", + "php": "^8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\JsonSchema\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Json Schema package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2026-02-24T14:03:17+00:00" + }, + { + "name": "laravel/boost", + "version": "v1.8.13", + "source": { + "type": "git", + "url": "https://github.com/laravel/boost.git", + "reference": "cdcb12114315491f72a2cecb5130d8b9dffa0103" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/boost/zipball/cdcb12114315491f72a2cecb5130d8b9dffa0103", + "reference": "cdcb12114315491f72a2cecb5130d8b9dffa0103", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.9", + "illuminate/console": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/contracts": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/routing": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/support": "^10.49.0|^11.45.3|^12.41.1", + "laravel/mcp": "^0.5.1", + "laravel/prompts": "0.1.25|^0.3.6", + "laravel/roster": "^0.2.9", + "php": "^8.1" }, "require-dev": { - "orchestra/testbench": "^6.0|^7.0|^8.0", - "phpstan/phpstan": "^1.10" + "laravel/pint": "^1.20.0", + "mockery/mockery": "^1.6.12", + "orchestra/testbench": "^8.36.0|^9.15.0|^10.6", + "pestphp/pest": "^2.36.0|^3.8.4|^4.1.5", + "phpstan/phpstan": "^2.1.27", + "rector/rector": "^2.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Boost\\BoostServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Boost\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.", + "homepage": "https://github.com/laravel/boost", + "keywords": [ + "ai", + "dev", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/boost/issues", + "source": "https://github.com/laravel/boost" + }, + "time": "2026-03-27T17:24:01+00:00" + }, + { + "name": "laravel/mcp", + "version": "v0.5.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/mcp.git", + "reference": "39b9791b989927642137dd5b55dde0529f1614f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/mcp/zipball/39b9791b989927642137dd5b55dde0529f1614f9", + "reference": "39b9791b989927642137dd5b55dde0529f1614f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/console": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/container": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/contracts": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/http": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/json-schema": "^12.41.1", + "illuminate/routing": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/support": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/validation": "^10.49.0|^11.45.3|^12.41.1", + "php": "^8.1" + }, + "require-dev": { + "laravel/pint": "^1.20", + "orchestra/testbench": "^8.36|^9.15|^10.8", + "pestphp/pest": "^2.36.0|^3.8.4|^4.1.0", + "phpstan/phpstan": "^2.1.27", + "rector/rector": "^2.2.4" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Mcp": "Laravel\\Mcp\\Server\\Facades\\Mcp" + }, + "providers": [ + "Laravel\\Mcp\\Server\\McpServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Mcp\\": "src/", + "Laravel\\Mcp\\Server\\": "src/Server/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Rapidly build MCP servers for your Laravel applications.", + "homepage": "https://github.com/laravel/mcp", + "keywords": [ + "laravel", + "mcp" + ], + "support": { + "issues": "https://github.com/laravel/mcp/issues", + "source": "https://github.com/laravel/mcp" + }, + "time": "2026-01-26T10:25:21+00:00" + }, + { + "name": "laravel/roster", + "version": "v0.2.9", + "source": { + "type": "git", + "url": "https://github.com/laravel/roster.git", + "reference": "82bbd0e2de614906811aebdf16b4305956816fa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/roster/zipball/82bbd0e2de614906811aebdf16b4305956816fa6", + "reference": "82bbd0e2de614906811aebdf16b4305956816fa6", + "shasum": "" + }, + "require": { + "illuminate/console": "^10.0|^11.0|^12.0", + "illuminate/contracts": "^10.0|^11.0|^12.0", + "illuminate/routing": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1|^8.2", + "symfony/yaml": "^6.4|^7.2" + }, + "require-dev": { + "laravel/pint": "^1.14", + "mockery/mockery": "^1.6", + "orchestra/testbench": "^8.22.0|^9.0|^10.0", + "pestphp/pest": "^2.0|^3.0", + "phpstan/phpstan": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Roster\\RosterServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Roster\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Detect packages & approaches in use within a Laravel project", + "homepage": "https://github.com/laravel/roster", + "keywords": [ + "dev", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/roster/issues", + "source": "https://github.com/laravel/roster" + }, + "time": "2025-10-20T09:56:46+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.56.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "f43426bb42a1cb7a51a3861d9138063e54766d28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/f43426bb42a1cb7a51a3861d9138063e54766d28", + "reference": "f43426bb42a1cb7a51a3861d9138063e54766d28", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/yaml": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.0" }, "bin": [ "bin/sail" @@ -9642,9 +9997,6 @@ "providers": [ "Laravel\\Sail\\SailServiceProvider" ] - }, - "branch-alias": { - "dev-master": "1.x-dev" } }, "autoload": { @@ -9671,76 +10023,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2023-09-11T17:37:09+00:00" - }, - { - "name": "maximebf/debugbar", - "version": "v1.23.6", - "source": { - "type": "git", - "url": "https://github.com/php-debugbar/php-debugbar.git", - "reference": "4b3d5f1afe09a7db5a9d3282890f49f6176d6542" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/4b3d5f1afe09a7db5a9d3282890f49f6176d6542", - "reference": "4b3d5f1afe09a7db5a9d3282890f49f6176d6542", - "shasum": "" - }, - "require": { - "php": "^7.2|^8", - "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4|^5|^6|^7" - }, - "require-dev": { - "dbrekelmans/bdi": "^1", - "phpunit/phpunit": "^8|^9", - "symfony/panther": "^1|^2.1", - "twig/twig": "^1.38|^2.7|^3.0" - }, - "suggest": { - "kriswallsmith/assetic": "The best way to manage assets", - "monolog/monolog": "Log using Monolog", - "predis/predis": "Redis storage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.23-dev" - } - }, - "autoload": { - "psr-4": { - "DebugBar\\": "src/DebugBar/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Maxime Bouroumeau-Fuseau", - "email": "maxime.bouroumeau@gmail.com", - "homepage": "http://maximebf.com" - }, - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "Debug bar in the browser for php application", - "homepage": "https://github.com/maximebf/php-debugbar", - "keywords": [ - "debug", - "debugbar" - ], - "support": { - "issues": "https://github.com/php-debugbar/php-debugbar/issues", - "source": "https://github.com/php-debugbar/php-debugbar/tree/v1.23.6" - }, - "abandoned": "php-debugbar/php-debugbar", - "time": "2025-02-13T12:22:36+00:00" + "time": "2026-04-01T15:17:32+00:00" }, { "name": "mockery/mockery", @@ -9887,34 +10170,40 @@ }, { "name": "nunomaduro/collision", - "version": "v5.11.0", + "version": "v7.12.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "8b610eef8582ccdc05d8f2ab23305e2d37049461" + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/8b610eef8582ccdc05d8f2ab23305e2d37049461", - "reference": "8b610eef8582ccdc05d8f2ab23305e2d37049461", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/995245421d3d7593a6960822063bdba4f5d7cf1a", + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a", "shasum": "" }, "require": { - "facade/ignition-contracts": "^1.0", - "filp/whoops": "^2.14.3", - "php": "^7.3 || ^8.0", - "symfony/console": "^5.0" + "filp/whoops": "^2.17.0", + "nunomaduro/termwind": "^1.17.0", + "php": "^8.1.0", + "symfony/console": "^6.4.17" + }, + "conflict": { + "laravel/framework": ">=11.0.0" }, "require-dev": { - "brianium/paratest": "^6.1", - "fideloper/proxy": "^4.4.1", - "fruitcake/laravel-cors": "^2.0.3", - "laravel/framework": "8.x-dev", - "nunomaduro/larastan": "^0.6.2", - "nunomaduro/mock-final-classes": "^1.0", - "orchestra/testbench": "^6.0", - "phpstan/phpstan": "^0.12.64", - "phpunit/phpunit": "^9.5.0" + "brianium/paratest": "^7.4.8", + "laravel/framework": "^10.48.29", + "laravel/pint": "^1.21.2", + "laravel/sail": "^1.41.0", + "laravel/sanctum": "^3.3.3", + "laravel/tinker": "^2.10.1", + "nunomaduro/larastan": "^2.10.0", + "orchestra/testbench-core": "^8.35.0", + "pestphp/pest": "^2.36.0", + "phpunit/phpunit": "^10.5.36", + "sebastian/environment": "^6.1.0", + "spatie/laravel-ignition": "^2.9.1" }, "type": "library", "extra": { @@ -9925,6 +10214,9 @@ } }, "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], "psr-4": { "NunoMaduro\\Collision\\": "src/" } @@ -9970,7 +10262,7 @@ "type": "patreon" } ], - "time": "2022-01-10T16:22:52+00:00" + "time": "2025-03-14T22:35:49+00:00" }, { "name": "phar-io/manifest", @@ -10090,6 +10382,80 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "php-debugbar/php-debugbar", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/php-debugbar/php-debugbar.git", + "reference": "abb9fa3c5c8dbe7efe03ddba56782917481de3e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/abb9fa3c5c8dbe7efe03ddba56782917481de3e8", + "reference": "abb9fa3c5c8dbe7efe03ddba56782917481de3e8", + "shasum": "" + }, + "require": { + "php": "^8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.4|^7.3|^8.0" + }, + "replace": { + "maximebf/debugbar": "self.version" + }, + "require-dev": { + "dbrekelmans/bdi": "^1", + "phpunit/phpunit": "^10", + "symfony/browser-kit": "^6.0|7.0", + "symfony/panther": "^1|^2.1", + "twig/twig": "^3.11.2" + }, + "suggest": { + "kriswallsmith/assetic": "The best way to manage assets", + "monolog/monolog": "Log using Monolog", + "predis/predis": "Redis storage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "DebugBar\\": "src/DebugBar/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maxime Bouroumeau-Fuseau", + "email": "maxime.bouroumeau@gmail.com", + "homepage": "http://maximebf.com" + }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Debug bar in the browser for php application", + "homepage": "https://github.com/php-debugbar/php-debugbar", + "keywords": [ + "debug", + "debug bar", + "debugbar", + "dev" + ], + "support": { + "issues": "https://github.com/php-debugbar/php-debugbar/issues", + "source": "https://github.com/php-debugbar/php-debugbar/tree/v2.2.6" + }, + "time": "2025-12-22T13:21:32+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -10145,16 +10511,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.10.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { @@ -10197,22 +10563,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "time": "2024-11-09T15:12:26+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -10244,22 +10610,22 @@ "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.3.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2025-08-30T15:50:23+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.32", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { @@ -10267,18 +10633,18 @@ "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.6", - "phpunit/php-text-template": "^2.0.4", - "sebastian/code-unit-reverse-lookup": "^2.0.3", - "sebastian/complexity": "^2.0.3", - "sebastian/environment": "^5.1.5", - "sebastian/lines-of-code": "^1.0.4", - "sebastian/version": "^3.0.2", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^10.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -10287,7 +10653,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.2.x-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -10316,7 +10682,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -10324,32 +10690,32 @@ "type": "github" } ], - "time": "2024-08-22T04:23:01+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -10376,7 +10742,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, "funding": [ { @@ -10384,28 +10751,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-pcntl": "*" @@ -10413,7 +10780,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -10439,7 +10806,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" }, "funding": [ { @@ -10447,32 +10814,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2023-02-03T06:56:09+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -10498,7 +10865,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, "funding": [ { @@ -10506,32 +10874,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -10557,7 +10925,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, "funding": [ { @@ -10565,24 +10933,23 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2023-02-03T06:57:52+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.25", + "version": "10.5.63", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "049c011e01be805202d8eebedef49f769a8ec7b7" + "reference": "33198268dad71e926626b618f3ec3966661e4d90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/049c011e01be805202d8eebedef49f769a8ec7b7", - "reference": "049c011e01be805202d8eebedef49f769a8ec7b7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90", + "reference": "33198268dad71e926626b618f3ec3966661e4d90", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -10592,27 +10959,26 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.32", - "phpunit/php-file-iterator": "^3.0.6", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.4", - "phpunit/php-timer": "^5.0.3", - "sebastian/cli-parser": "^1.0.2", - "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.9", - "sebastian/diff": "^4.0.6", - "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.8", - "sebastian/object-enumerator": "^4.0.4", - "sebastian/resource-operations": "^3.0.4", - "sebastian/type": "^3.2.1", - "sebastian/version": "^3.0.2" + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.5", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.4", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" }, "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "ext-soap": "To be able to generate mocks based on WSDL files" }, "bin": [ "phpunit" @@ -10620,7 +10986,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -10652,7 +11018,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.25" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63" }, "funding": [ { @@ -10676,32 +11042,32 @@ "type": "tidelift" } ], - "time": "2025-08-20T14:38:31+00:00" + "time": "2026-01-27T05:48:37+00:00" }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -10724,7 +11090,8 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" }, "funding": [ { @@ -10732,32 +11099,32 @@ "type": "github" } ], - "time": "2024-03-02T06:27:43+00:00" + "time": "2024-03-02T07:12:49+00:00" }, { "name": "sebastian/code-unit", - "version": "1.0.8", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -10780,7 +11147,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { @@ -10788,32 +11155,32 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -10835,7 +11202,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { @@ -10843,34 +11210,36 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.9", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", - "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -10909,7 +11278,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" }, "funding": [ { @@ -10929,33 +11299,33 @@ "type": "tidelift" } ], - "time": "2025-08-10T06:51:50+00:00" + "time": "2026-01-24T09:25:16+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -10978,7 +11348,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -10986,33 +11357,33 @@ "type": "github" } ], - "time": "2023-12-22T06:19:30+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -11044,7 +11415,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" }, "funding": [ { @@ -11052,27 +11424,27 @@ "type": "github" } ], - "time": "2024-03-02T06:30:58+00:00" + "time": "2024-03-02T07:15:17+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "6.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-posix": "*" @@ -11080,7 +11452,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -11099,7 +11471,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -11107,7 +11479,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -11115,34 +11488,34 @@ "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "0735b90f4da94969541dac1da743446e276defa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -11184,46 +11557,56 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" }, "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/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:09:11+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.8", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11242,59 +11625,48 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" }, "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/global-state", - "type": "tidelift" } ], - "time": "2025-08-10T07:10:35+00:00" + "time": "2024-03-02T07:19:19+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -11317,7 +11689,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -11325,34 +11698,34 @@ "type": "github" } ], - "time": "2023-12-22T06:20:34+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -11374,7 +11747,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -11382,32 +11755,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -11429,7 +11802,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { @@ -11437,32 +11810,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.6", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", - "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -11492,7 +11865,8 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" }, "funding": [ { @@ -11512,86 +11886,32 @@ "type": "tidelift" } ], - "time": "2025-08-10T06:57:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2025-08-10T07:50:56+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -11614,7 +11934,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { @@ -11622,29 +11942,29 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -11667,7 +11987,7 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { @@ -11675,32 +11995,416 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { - "name": "symfony/yaml", - "version": "v6.4.25", + "name": "spatie/backtrace", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "e54b060bc9c3dc3d4258bf0d165d0064e755f565" + "url": "https://github.com/spatie/backtrace.git", + "reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e54b060bc9c3dc3d4258bf0d165d0064e755f565", - "reference": "e54b060bc9c3dc3d4258bf0d165d0064e755f565", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/8ffe78be5ed355b5009e3dd989d183433e9a5adc", + "reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc", "shasum": "" }, "require": { - "php": ">=8.1", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-json": "*", + "laravel/serializable-closure": "^1.3 || ^2.0", + "phpunit/phpunit": "^9.3 || ^11.4.3", + "spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6", + "symfony/var-dumper": "^5.1|^6.0|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Backtrace\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van de Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A better backtrace", + "homepage": "https://github.com/spatie/backtrace", + "keywords": [ + "Backtrace", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/backtrace/issues", + "source": "https://github.com/spatie/backtrace/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/spatie", + "type": "github" + }, + { + "url": "https://spatie.be/open-source/support-us", + "type": "other" + } + ], + "time": "2026-03-11T13:48:28+00:00" + }, + { + "name": "spatie/error-solutions", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/error-solutions.git", + "reference": "e495d7178ca524f2dd0fe6a1d99a1e608e1c9936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/error-solutions/zipball/e495d7178ca524f2dd0fe6a1d99a1e608e1c9936", + "reference": "e495d7178ca524f2dd0fe6a1d99a1e608e1c9936", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "illuminate/broadcasting": "^10.0|^11.0|^12.0", + "illuminate/cache": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "livewire/livewire": "^2.11|^3.5.20", + "openai-php/client": "^0.10.1", + "orchestra/testbench": "8.22.3|^9.0|^10.0", + "pestphp/pest": "^2.20|^3.0", + "phpstan/phpstan": "^2.1", + "psr/simple-cache": "^3.0", + "psr/simple-cache-implementation": "^3.0", + "spatie/ray": "^1.28", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Ignition\\": "legacy/ignition", + "Spatie\\ErrorSolutions\\": "src", + "Spatie\\LaravelIgnition\\": "legacy/laravel-ignition" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "This is my package error-solutions", + "homepage": "https://github.com/spatie/error-solutions", + "keywords": [ + "error-solutions", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/error-solutions/issues", + "source": "https://github.com/spatie/error-solutions/tree/1.1.3" + }, + "funding": [ + { + "url": "https://github.com/Spatie", + "type": "github" + } + ], + "time": "2025-02-14T12:29:50+00:00" + }, + { + "name": "spatie/flare-client-php", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/flare-client-php.git", + "reference": "fb3ffb946675dba811fbde9122224db2f84daca9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/fb3ffb946675dba811fbde9122224db2f84daca9", + "reference": "fb3ffb946675dba811fbde9122224db2f84daca9", + "shasum": "" + }, + "require": { + "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0", + "spatie/backtrace": "^1.6.1", + "symfony/http-foundation": "^5.2|^6.0|^7.0|^8.0", + "symfony/mime": "^5.2|^6.0|^7.0|^8.0", + "symfony/process": "^5.2|^6.0|^7.0|^8.0", + "symfony/var-dumper": "^5.2|^6.0|^7.0|^8.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.5.0", + "pestphp/pest": "^1.20|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/pest-plugin-snapshots": "^1.0|^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\FlareClient\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Send PHP errors to Flare", + "homepage": "https://github.com/spatie/flare-client-php", + "keywords": [ + "exception", + "flare", + "reporting", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/flare-client-php/issues", + "source": "https://github.com/spatie/flare-client-php/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2026-03-17T08:06:16+00:00" + }, + { + "name": "spatie/ignition", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/ignition.git", + "reference": "b59385bb7aa24dae81bcc15850ebecfda7b40838" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/ignition/zipball/b59385bb7aa24dae81bcc15850ebecfda7b40838", + "reference": "b59385bb7aa24dae81bcc15850ebecfda7b40838", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.0", + "spatie/backtrace": "^1.7.1", + "spatie/error-solutions": "^1.1.2", + "spatie/flare-client-php": "^1.9", + "symfony/console": "^5.4.42|^6.0|^7.0|^8.0", + "symfony/http-foundation": "^5.4.42|^6.0|^7.0|^8.0", + "symfony/mime": "^5.4.42|^6.0|^7.0|^8.0", + "symfony/var-dumper": "^5.4.42|^6.0|^7.0|^8.0" + }, + "require-dev": { + "illuminate/cache": "^9.52|^10.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.4", + "pestphp/pest": "^1.20|^2.0|^3.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "psr/simple-cache-implementation": "*", + "symfony/cache": "^5.4.38|^6.0|^7.0|^8.0", + "symfony/process": "^5.4.35|^6.0|^7.0|^8.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\Ignition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for PHP applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/ignition/issues", + "source": "https://github.com/spatie/ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2026-03-17T10:51:08+00:00" + }, + { + "name": "spatie/laravel-ignition", + "version": "2.9.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-ignition.git", + "reference": "1baee07216d6748ebd3a65ba97381b051838707a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/1baee07216d6748ebd3a65ba97381b051838707a", + "reference": "1baee07216d6748ebd3a65ba97381b051838707a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1", + "spatie/ignition": "^1.15", + "symfony/console": "^6.2.3|^7.0", + "symfony/var-dumper": "^6.2.3|^7.0" + }, + "require-dev": { + "livewire/livewire": "^2.11|^3.3.5", + "mockery/mockery": "^1.5.1", + "openai-php/client": "^0.8.1|^0.10", + "orchestra/testbench": "8.22.3|^9.0|^10.0", + "pestphp/pest": "^2.34|^3.7", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan-deprecation-rules": "^1.1.1|^2.0", + "phpstan/phpstan-phpunit": "^1.3.16|^2.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "psr/simple-cache-implementation": "Needed to cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" + }, + "providers": [ + "Spatie\\LaravelIgnition\\IgnitionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\LaravelIgnition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for Laravel applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/laravel-ignition/issues", + "source": "https://github.com/spatie/laravel-ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-02-20T13:13:55+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c58fdf7b3d6c2995368264c49e4e8b05bcff2883", + "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883", + "shasum": "" + }, + "require": { + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -11731,7 +12435,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.25" + "source": "https://github.com/symfony/yaml/tree/v7.4.8" }, "funding": [ { @@ -11751,20 +12455,20 @@ "type": "tidelift" } ], - "time": "2025-08-26T16:59:00+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -11793,7 +12497,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -11801,7 +12505,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], @@ -11810,8 +12514,8 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.3|^8.0" + "php": "^8.1|^8.2|^8.3" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/app.php b/config/app.php index ad6abf1..ab7a418 100755 --- a/config/app.php +++ b/config/app.php @@ -116,6 +116,7 @@ return [ 'cipher' => 'AES-256-CBC', + 'success_key' => env('SUCCESS_KEY'), /* |-------------------------------------------------------------------------- | Autoloaded Service Providers diff --git a/config/filesystems.php b/config/filesystems.php index 2318ca2..97762c8 100755 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -72,6 +72,12 @@ return [ 'url' => env('APP_URL').'/storage/booking', 'visibility' => 'public', ], + 'offer' => [ + 'driver' => 'local', + 'root' => storage_path('app/offer'), + 'url' => env('APP_URL').'/storage/offer', + 'visibility' => 'public', + ], 'general' => [ 'driver' => 'local', 'root' => storage_path('app/general'), diff --git a/config/trustedproxy.php b/config/trustedproxy.php index acda8d5..7400d5d 100644 --- a/config/trustedproxy.php +++ b/config/trustedproxy.php @@ -39,7 +39,12 @@ return [ * * @link https://symfony.com/doc/current/deployment/proxies.html */ - 'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_ALL, + 'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_FOR | + Illuminate\Http\Request::HEADER_X_FORWARDED_HOST | + Illuminate\Http\Request::HEADER_X_FORWARDED_PORT | + Illuminate\Http\Request::HEADER_X_FORWARDED_PROTO | + Illuminate\Http\Request::HEADER_X_FORWARDED_PREFIX | + Illuminate\Http\Request::HEADER_X_FORWARDED_AWS_ELB, ]; diff --git a/database/factories/BookingFactory.php b/database/factories/BookingFactory.php new file mode 100644 index 0000000..29244ea --- /dev/null +++ b/database/factories/BookingFactory.php @@ -0,0 +1,69 @@ + + */ +class BookingFactory extends Factory +{ + protected $model = Booking::class; + + public function definition(): array + { + $start = fake()->dateTimeBetween('+1 month', '+6 months'); + $end = fake()->dateTimeBetween($start, '+12 months'); + + return [ + 'customer_id' => CustomerFactory::new(), + 'lead_id' => LeadFactory::new(), + 'booking_date' => now()->format('Y-m-d'), + 'start_date' => $start->format('Y-m-d'), + 'end_date' => $end->format('Y-m-d'), + 'sf_guard_user_id' => null, + 'branch_id' => null, + 'service_fee' => 0.0, + 'travel_country_id' => null, + 'travel_category_id' => null, + 'pax' => 2, + 'coupon_id' => null, + 'title' => fake()->sentence(4), + 'travel_number' => strtoupper(fake()->bothify('??####')), + 'participant_name' => fake()->lastName(), + 'participant_firstname' => fake()->firstName(), + 'participant_birthdate' => fake()->dateTimeBetween('-60 years', '-18 years')->format('Y-m-d'), + 'participant_salutation_id' => 1, + 'ev_number' => '', + 'merlin_knr' => '', + 'merlin_order_number' => '', + 'travel_company_id' => null, + 'travel_documents' => false, + 'price' => fake()->randomFloat(2, 500, 5000), + 'price_total' => 0.0, + 'deposit_total' => 0.0, + 'final_payment' => 0.0, + 'final_payment_date' => null, + 'travelagenda_id' => null, + 'website_id' => null, + 'new_drafts' => false, + ]; + } + + public function canceled(): static + { + return $this->state(fn (array $attributes) => [ + 'canceled' => 1.0, + ]); + } + + public function withPrice(float $price): static + { + return $this->state(fn (array $attributes) => [ + 'price' => $price, + 'price_total' => $price, + ]); + } +} diff --git a/database/factories/CustomerFactory.php b/database/factories/CustomerFactory.php new file mode 100644 index 0000000..5f91720 --- /dev/null +++ b/database/factories/CustomerFactory.php @@ -0,0 +1,50 @@ + + */ +class CustomerFactory extends Factory +{ + protected $model = Customer::class; + + public function definition(): array + { + return [ + 'salutation_id' => 1, + 'title' => '', + 'name' => fake()->lastName(), + 'firstname' => fake()->firstName(), + 'birthdate' => fake()->dateTimeBetween('-80 years', '-18 years')->format('Y-m-d'), + 'company' => '', + 'street' => fake()->streetAddress(), + 'zip' => fake()->postcode(), + 'city' => fake()->city(), + 'email' => fake()->unique()->safeEmail(), + 'phone' => fake()->phoneNumber(), + 'phonebusiness' => '', + 'phonemobile' => fake()->phoneNumber(), + 'fax' => '', + 'bank' => '', + 'bank_code' => '', + 'bank_account_number' => '', + 'credit_card_type_id' => null, + 'credit_card_number' => '', + 'credit_card_expiration_date'=> null, + 'participants_remarks' => '', + 'miscellaneous_remarks' => '', + 'country_id' => null, + ]; + } + + public function company(): static + { + return $this->state(fn (array $attributes) => [ + 'company' => fake()->company(), + ]); + } +} diff --git a/database/factories/LeadFactory.php b/database/factories/LeadFactory.php new file mode 100644 index 0000000..2273966 --- /dev/null +++ b/database/factories/LeadFactory.php @@ -0,0 +1,60 @@ + + */ +class LeadFactory extends Factory +{ + protected $model = Lead::class; + + public function definition(): array + { + $start = fake()->dateTimeBetween('+1 month', '+6 months'); + $end = fake()->dateTimeBetween($start, '+12 months'); + + return [ + 'customer_id' => CustomerFactory::new(), + 'request_date' => now()->format('Y-m-d'), + 'travelperiod_start' => $start->format('Y-m-d'), + 'travelperiod_end' => $end->format('Y-m-d'), + 'travelperiod_length' => (int) $start->diff($end)->days, + 'travelcountry_id' => null, + 'travelagenda_id' => null, + 'remarks' => '', + 'sf_guard_user_id' => null, + 'is_closed' => false, + 'initialcontacttype_id' => null, + 'searchengine_id' => null, + 'searchengine_keywords' => '', + 'status_id' => 1, + 'next_due_date' => now()->addDays(7)->format('Y-m-d'), + 'website_id' => null, + 'travelcategory_id' => null, + 'price' => 0.0, + 'pax' => 2, + 'participant_name' => fake()->lastName(), + 'participant_firstname' => fake()->firstName(), + 'participant_birthdate' => fake()->dateTimeBetween('-60 years', '-18 years')->format('Y-m-d'), + 'participant_salutation_id' => 1, + ]; + } + + public function closed(): static + { + return $this->state(fn (array $attributes) => [ + 'is_closed' => true, + ]); + } + + public function withPrice(float $price): static + { + return $this->state(fn (array $attributes) => [ + 'price' => $price, + ]); + } +} diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index facf233..67ccaa0 100755 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -1,23 +1,43 @@ define(App\User::class, function (Faker $faker) { - return [ - 'name' => $faker->name, - 'email' => $faker->unique()->safeEmail, - 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret - 'remember_token' => str_random(10), - ]; -}); +/** + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\User> + */ +class UserFactory extends Factory +{ + protected $model = User::class; + + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'password' => Hash::make('password'), + 'remember_token' => Str::random(10), + 'active' => 1, + 'admin' => 0, + 'confirmed' => 1, + ]; + } + + public function admin(): static + { + return $this->state(fn (array $attributes) => [ + 'admin' => 1, + ]); + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'active' => 0, + ]); + } +} diff --git a/database/migrations/2025_04_15_100001_phase1_add_merge_fields_to_customer_table.php b/database/migrations/2025_04_15_100001_phase1_add_merge_fields_to_customer_table.php new file mode 100644 index 0000000..2c6a17b --- /dev/null +++ b/database/migrations/2025_04_15_100001_phase1_add_merge_fields_to_customer_table.php @@ -0,0 +1,36 @@ +unsignedBigInteger('merged_into_id')->nullable()->after('id'); + $table->dateTime('merged_at')->nullable()->after('merged_into_id'); + + $table->index('merged_into_id', 'customer_merged_into_id_idx'); + }); + } + + public function down(): void + { + Schema::table('customer', function (Blueprint $table) { + $table->dropIndex('customer_merged_into_id_idx'); + $table->dropColumn(['merged_into_id', 'merged_at']); + }); + } +}; diff --git a/database/migrations/2025_04_15_100002_phase1_add_soft_delete_to_customer_table.php b/database/migrations/2025_04_15_100002_phase1_add_soft_delete_to_customer_table.php new file mode 100644 index 0000000..464aa32 --- /dev/null +++ b/database/migrations/2025_04_15_100002_phase1_add_soft_delete_to_customer_table.php @@ -0,0 +1,34 @@ +softDeletes(); + }); + } + + public function down(): void + { + Schema::table('customer', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + } +}; diff --git a/database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php b/database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php new file mode 100644 index 0000000..17cd0b4 --- /dev/null +++ b/database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php @@ -0,0 +1,30 @@ +dropForeign('booking_lead_id_lead_id'); + }); + + DB::statement('ALTER TABLE `booking` RENAME COLUMN `lead_id` TO `inquiry_id`'); + + Schema::table('booking', function (Blueprint $table) { + // Neuen FK auf umbenannte Tabelle setzen + $table->foreign('inquiry_id', 'booking_inquiry_id_inquiries_id') + ->references('id')->on('inquiries') + ->onDelete('no action') + ->onUpdate('no action'); + }); + } + + public function down(): void + { + Schema::table('booking', function (Blueprint $table) { + $table->dropForeign('booking_inquiry_id_inquiries_id'); + }); + + DB::statement('ALTER TABLE `booking` RENAME COLUMN `inquiry_id` TO `lead_id`'); + + Schema::table('booking', function (Blueprint $table) { + $table->foreign('lead_id', 'booking_lead_id_lead_id') + ->references('id')->on('lead') + ->onDelete('no action') + ->onUpdate('no action'); + }); + } +}; diff --git a/database/migrations/2025_04_15_300001_phase3_create_participants_unified_table.php b/database/migrations/2025_04_15_300001_phase3_create_participants_unified_table.php new file mode 100644 index 0000000..70a1c2f --- /dev/null +++ b/database/migrations/2025_04_15_300001_phase3_create_participants_unified_table.php @@ -0,0 +1,123 @@ +id(); + + // Kontext-FKs — genau einer ist gesetzt, der andere NULL + $table->unsignedBigInteger('inquiry_id')->nullable(); + $table->unsignedBigInteger('booking_id')->nullable(); + + // Teilnehmerdaten + $table->string('participant_name')->nullable(); + $table->string('participant_firstname')->nullable(); + $table->date('participant_birthdate')->nullable(); + $table->unsignedBigInteger('participant_salutation_id')->nullable(); + $table->boolean('participant_child')->default(false); + $table->integer('nationality_id')->nullable(); + + // Nur bei Buchungs-Teilnehmern + $table->boolean('participant_pass')->default(false); + $table->boolean('participant_storno')->default(false); + + // Markiert den Hauptreisenden aus lead.participant_name + $table->boolean('is_lead_contact')->default(false); + + $table->timestamps(); + + $table->index('inquiry_id'); + $table->index('booking_id'); + + $table->foreign('inquiry_id', 'pu_inquiry_id_fk') + ->references('id')->on('inquiries') + ->onDelete('cascade'); + + $table->foreign('booking_id', 'pu_booking_id_fk') + ->references('id')->on('booking') + ->onDelete('cascade'); + + $table->foreign('participant_salutation_id', 'pu_salutation_id_fk') + ->references('id')->on('salutation') + ->onDelete('set null'); + }); + + // ── Daten migrieren ───────────────────────────────────────────────── + + // 1. Aus lead_participant → inquiry_id gesetzt + DB::statement(" + INSERT INTO participants_unified + (inquiry_id, booking_id, participant_name, participant_firstname, + participant_birthdate, participant_salutation_id, participant_child, + nationality_id, is_lead_contact, created_at, updated_at) + SELECT + lead_id, NULL, + participant_name, participant_firstname, + participant_birthdate, participant_salutation_id, participant_child, + nationality_id, 0, + NOW(), NOW() + FROM lead_participant + "); + + // 2. Aus lead.participant_name → Hauptreisende-Datensätze (is_lead_contact = 1) + DB::statement(" + INSERT INTO participants_unified + (inquiry_id, booking_id, participant_name, participant_firstname, + participant_birthdate, participant_salutation_id, participant_child, + nationality_id, is_lead_contact, created_at, updated_at) + SELECT + id, NULL, + participant_name, participant_firstname, + participant_birthdate, participant_salutation_id, 0, + NULL, 1, + NOW(), NOW() + FROM inquiries + WHERE participant_name IS NOT NULL + AND participant_name != '' + "); + + // 3. Aus participant → booking_id gesetzt + DB::statement(" + INSERT INTO participants_unified + (inquiry_id, booking_id, participant_name, participant_firstname, + participant_birthdate, participant_salutation_id, participant_child, + nationality_id, participant_pass, participant_storno, + is_lead_contact, created_at, updated_at) + SELECT + NULL, booking_id, + participant_name, participant_firstname, + participant_birthdate, participant_salutation_id, participant_child, + nationality_id, participant_pass, participant_storno, + 0, NOW(), NOW() + FROM participant + "); + } + + public function down(): void + { + Schema::dropIfExists('participants_unified'); + } +}; diff --git a/database/migrations/2025_04_15_300002_phase3_drop_old_participant_tables.php b/database/migrations/2025_04_15_300002_phase3_drop_old_participant_tables.php new file mode 100644 index 0000000..3f2cbec --- /dev/null +++ b/database/migrations/2025_04_15_300002_phase3_drop_old_participant_tables.php @@ -0,0 +1,40 @@ +id(); + + // Kontext-FKs — bei customer_mails können beide gesetzt sein + $table->unsignedBigInteger('inquiry_id')->nullable(); + $table->unsignedBigInteger('booking_id')->nullable(); + $table->unsignedBigInteger('contact_id')->nullable(); + + // Reply-Chain — kein FK-Constraint (cross-table Remapping) + $table->unsignedBigInteger('reply_id')->nullable(); + + // Herkunft für Remapping und spätere Analyse + $table->enum('legacy_source', ['lead_mail', 'customer_mail']); + $table->unsignedBigInteger('legacy_id'); + + // Mail-Felder (identisch in beiden Quell-Tabellen) + $table->boolean('is_answer')->default(false); + $table->string('email')->nullable(); + $table->text('recipient')->nullable(); + $table->text('cc')->nullable(); + $table->text('bcc')->nullable(); + $table->string('subject')->nullable(); + $table->text('message')->nullable(); + $table->boolean('dir')->default(false); + $table->unsignedBigInteger('subdir')->nullable(); + + // Nur in customer_mails vorhanden + $table->unsignedBigInteger('travel_country_id')->nullable(); + + $table->boolean('draft')->default(false); + $table->boolean('important')->default(false); + $table->boolean('send')->default(false); + $table->boolean('fail')->default(false); + $table->text('error')->nullable(); + $table->text('forward')->nullable(); + + $table->dateTime('sent_at')->nullable(); + $table->dateTime('scheduled_at')->nullable(); + $table->dateTime('delivered_at')->nullable(); + + $table->timestamps(); + + $table->index('inquiry_id'); + $table->index('booking_id'); + $table->index('contact_id'); + $table->index('reply_id'); + $table->index(['legacy_source', 'legacy_id'], 'comm_legacy_idx'); + + $table->foreign('inquiry_id', 'comm_inquiry_id_fk') + ->references('id')->on('inquiries') + ->onDelete('cascade'); + + $table->foreign('booking_id', 'comm_booking_id_fk') + ->references('id')->on('booking') + ->onDelete('cascade'); + + $table->foreign('contact_id', 'comm_contact_id_fk') + ->references('id')->on('contacts') + ->onDelete('set null'); + }); + + // ── Daten migrieren ───────────────────────────────────────────────── + + // 1. Aus lead_mails → inquiry_id gesetzt + DB::statement(" + INSERT INTO communications + (inquiry_id, booking_id, contact_id, is_answer, reply_id, + legacy_source, legacy_id, + email, recipient, cc, bcc, subject, message, + dir, subdir, travel_country_id, + draft, important, send, fail, error, forward, + sent_at, scheduled_at, delivered_at, created_at, updated_at) + SELECT + lead_id, NULL, customer_id, is_answer, reply_id, + 'lead_mail', id, + email, recipient, cc, bcc, subject, message, + dir, subdir, NULL, + draft, important, send, fail, error, forward, + sent_at, scheduled_at, delivered_at, created_at, updated_at + FROM lead_mails + "); + + // 2. Aus customer_mails → booking_id gesetzt (kann zusätzlich lead_id haben) + DB::statement(" + INSERT INTO communications + (inquiry_id, booking_id, contact_id, is_answer, reply_id, + legacy_source, legacy_id, + email, recipient, cc, bcc, subject, message, + dir, subdir, travel_country_id, + draft, important, send, fail, error, forward, + sent_at, scheduled_at, delivered_at, created_at, updated_at) + SELECT + lead_id, booking_id, customer_id, is_answer, reply_id, + 'customer_mail', id, + email, recipient, cc, bcc, subject, message, + dir, subdir, travel_country_id, + draft, important, send, fail, error, forward, + sent_at, scheduled_at, delivered_at, created_at, updated_at + FROM customer_mails + "); + + // 3. Reply-IDs remappen: alte source-table-IDs auf neue communications-IDs umstellen + // Funktioniert weil reply_id immer auf eine Mail derselben Quell-Tabelle zeigt. + DB::statement(" + UPDATE communications c1 + INNER JOIN communications c2 + ON c1.legacy_source = c2.legacy_source + AND c2.legacy_id = c1.reply_id + SET c1.reply_id = c2.id + WHERE c1.reply_id IS NOT NULL + "); + } + + public function down(): void + { + Schema::dropIfExists('communications'); + } +}; diff --git a/database/migrations/2025_04_15_400002_phase4_create_notices_table.php b/database/migrations/2025_04_15_400002_phase4_create_notices_table.php new file mode 100644 index 0000000..624c891 --- /dev/null +++ b/database/migrations/2025_04_15_400002_phase4_create_notices_table.php @@ -0,0 +1,94 @@ +id(); + + // Kontext-FKs — genau einer ist gesetzt + $table->unsignedBigInteger('inquiry_id')->nullable(); + $table->unsignedBigInteger('booking_id')->nullable(); + + // Benutzer-Referenzen (integer wie in den Quell-Tabellen) + $table->unsignedInteger('from_user_id'); + $table->unsignedInteger('to_user_id')->nullable(); + + $table->text('message')->nullable(); + $table->boolean('show')->default(false); + $table->boolean('important')->default(false); + $table->dateTime('edit_at')->nullable(); + + $table->timestamps(); + + $table->index('inquiry_id'); + $table->index('booking_id'); + $table->index('from_user_id'); + $table->index('to_user_id'); + + $table->foreign('inquiry_id', 'notices_inquiry_id_fk') + ->references('id')->on('inquiries') + ->onDelete('cascade'); + + $table->foreign('booking_id', 'notices_booking_id_fk') + ->references('id')->on('booking') + ->onDelete('cascade'); + + $table->foreign('from_user_id', 'notices_from_user_id_fk') + ->references('id')->on('users') + ->onDelete('cascade'); + + $table->foreign('to_user_id', 'notices_to_user_id_fk') + ->references('id')->on('users') + ->onDelete('cascade'); + }); + + // ── Daten migrieren ───────────────────────────────────────────────── + + // 1. Aus lead_notices → inquiry_id gesetzt + DB::statement(" + INSERT INTO notices + (inquiry_id, booking_id, from_user_id, to_user_id, + message, show, important, edit_at, created_at, updated_at) + SELECT + lead_id, NULL, from_user_id, to_user_id, + message, show, important, edit_at, created_at, updated_at + FROM lead_notices + "); + + // 2. Aus booking_notices → booking_id gesetzt + DB::statement(" + INSERT INTO notices + (inquiry_id, booking_id, from_user_id, to_user_id, + message, show, important, edit_at, created_at, updated_at) + SELECT + NULL, booking_id, from_user_id, to_user_id, + message, show, important, edit_at, created_at, updated_at + FROM booking_notices + "); + } + + public function down(): void + { + Schema::dropIfExists('notices'); + } +}; diff --git a/database/migrations/2025_04_15_400003_phase4_create_attachments_table.php b/database/migrations/2025_04_15_400003_phase4_create_attachments_table.php new file mode 100644 index 0000000..7f44feb --- /dev/null +++ b/database/migrations/2025_04_15_400003_phase4_create_attachments_table.php @@ -0,0 +1,118 @@ +id(); + + // Kontext-FKs + $table->unsignedBigInteger('inquiry_id')->nullable(); + $table->unsignedBigInteger('booking_id')->nullable(); + + // Verknüpfung zur dazugehörigen Mail (nur bei Anfrage-Anhängen) + $table->unsignedBigInteger('communication_id')->nullable(); + + // Datei-Metadaten + $table->string('identifier')->nullable(); + $table->string('filename'); + $table->string('dir')->nullable(); + $table->string('original_name')->nullable(); + $table->string('ext')->nullable(); + $table->string('mime_type')->nullable(); + $table->unsignedInteger('size')->nullable(); + + // Herkunft für spätere Analyse / Rollback + $table->enum('legacy_source', ['lead_file', 'booking_file']); + $table->unsignedBigInteger('legacy_id'); + + $table->timestamps(); + + $table->index('inquiry_id'); + $table->index('booking_id'); + $table->index('communication_id'); + $table->index('identifier', 'attachments_identifier_idx'); + $table->index(['legacy_source', 'legacy_id'], 'attachments_legacy_idx'); + + $table->foreign('inquiry_id', 'att_inquiry_id_fk') + ->references('id')->on('inquiries') + ->onDelete('cascade'); + + $table->foreign('booking_id', 'att_booking_id_fk') + ->references('id')->on('booking') + ->onDelete('cascade'); + + $table->foreign('communication_id', 'att_communication_id_fk') + ->references('id')->on('communications') + ->onDelete('set null'); + }); + + // ── Daten migrieren ───────────────────────────────────────────────── + + // 1. Aus lead_files → inquiry_id gesetzt; communication_id wird unten gesetzt + DB::statement(" + INSERT INTO attachments + (inquiry_id, booking_id, communication_id, + identifier, filename, dir, original_name, ext, mime_type, size, + legacy_source, legacy_id, created_at, updated_at) + SELECT + lead_id, NULL, NULL, + identifier, filename, dir, original_name, ext, mine, size, + 'lead_file', id, created_at, updated_at + FROM lead_files + "); + + // 2. Aus booking_files → booking_id gesetzt + DB::statement(" + INSERT INTO attachments + (inquiry_id, booking_id, communication_id, + identifier, filename, dir, original_name, ext, mime_type, size, + legacy_source, legacy_id, created_at, updated_at) + SELECT + NULL, booking_id, NULL, + identifier, filename, dir, original_name, ext, mine, size, + 'booking_file', id, created_at, updated_at + FROM booking_files + "); + + // 3. communication_id für lead_files setzen (sofern lead_mail_id gesetzt war) + // Nutzt legacy_source + legacy_id der communications-Tabelle. + DB::statement(" + UPDATE attachments a + INNER JOIN lead_files lf ON lf.id = a.legacy_id + INNER JOIN communications c + ON c.legacy_source = 'lead_mail' + AND c.legacy_id = lf.lead_mail_id + SET a.communication_id = c.id + WHERE a.legacy_source = 'lead_file' + AND lf.lead_mail_id IS NOT NULL + "); + } + + public function down(): void + { + Schema::dropIfExists('attachments'); + } +}; diff --git a/database/migrations/2025_04_15_400004_phase4_drop_old_communication_tables.php b/database/migrations/2025_04_15_400004_phase4_drop_old_communication_tables.php new file mode 100644 index 0000000..bbdff3f --- /dev/null +++ b/database/migrations/2025_04_15_400004_phase4_drop_old_communication_tables.php @@ -0,0 +1,45 @@ +dropForeign('lead_files_lead_mail_id_foreign'); + }); + } + + Schema::dropIfExists('lead_mails'); + Schema::dropIfExists('customer_mails'); + } + + public function down(): void + { + throw new \RuntimeException( + 'Phase 4 Schritt 2a kann nicht automatisch zurückgerollt werden. ' . + 'Bitte Datenbank-Backup einspielen.' + ); + } +}; diff --git a/database/migrations/2025_04_15_400005_phase4_drop_old_notice_tables.php b/database/migrations/2025_04_15_400005_phase4_drop_old_notice_tables.php new file mode 100644 index 0000000..d50006d --- /dev/null +++ b/database/migrations/2025_04_15_400005_phase4_drop_old_notice_tables.php @@ -0,0 +1,35 @@ +id(); + $t->string('offer_number', 32)->unique(); + + $t->foreignId('contact_id') + ->constrained('contacts') + ->restrictOnDelete(); + + $t->foreignId('inquiry_id') + ->nullable() + ->constrained('inquiries') + ->nullOnDelete(); + + $t->foreignId('booking_id') + ->nullable() + ->constrained('booking') + ->nullOnDelete(); + + $t->enum('status', [ + 'draft', + 'sent', + 'accepted', + 'declined', + 'expired', + 'withdrawn', + ])->default('draft'); + + // FK wird in 2026_04_17_100007 nachträglich gesetzt + $t->unsignedBigInteger('current_version_id')->nullable(); + + $t->foreignId('created_by')->constrained('users'); + + $t->timestamps(); + $t->softDeletes(); + + $t->index(['status', 'contact_id']); + $t->index('inquiry_id'); + }); + } + + public function down(): void + { + Schema::dropIfExists('offers'); + } +}; diff --git a/database/migrations/2026_04_17_100002_create_offer_versions_table.php b/database/migrations/2026_04_17_100002_create_offer_versions_table.php new file mode 100644 index 0000000..2cc6e30 --- /dev/null +++ b/database/migrations/2026_04_17_100002_create_offer_versions_table.php @@ -0,0 +1,81 @@ +id(); + + $t->foreignId('offer_id') + ->constrained('offers') + ->cascadeOnDelete(); + + $t->unsignedInteger('version_no'); + + $t->enum('status', [ + 'draft', + 'sent', + 'accepted', + 'declined', + 'expired', + 'superseded', + ])->default('draft'); + + $t->date('valid_until')->nullable(); + $t->decimal('total_price', 10, 2)->default(0); + + $t->string('headline')->nullable(); + $t->text('intro_text')->nullable(); + $t->longText('itinerary_text')->nullable(); + $t->text('closing_text')->nullable(); + + // offer_templates wird in Migration 4 erzeugt — FK dort, + // hier zunächst nullable + FK wird über Migration 4 nachgeholt + $t->unsignedBigInteger('template_id')->nullable(); + + $t->string('pdf_path')->nullable(); + $t->boolean('pdf_archived')->default(false); + + $t->dateTime('sent_at')->nullable(); + $t->dateTime('accepted_at')->nullable(); + + $t->enum('accepted_via', [ + 'customer_link', + 'admin', + 'email', + ])->nullable(); + + // Referenz auf zentral hinterlegte Dokument-Vorlagen, + // die mit dieser Version (als Anhang) verknüpft sind + $t->json('template_document_ids')->nullable(); + + $t->foreignId('created_by')->constrained('users'); + + $t->timestamps(); + + $t->unique(['offer_id', 'version_no']); + $t->index(['offer_id', 'status']); + }); + } + + public function down(): void + { + Schema::dropIfExists('offer_versions'); + } +}; diff --git a/database/migrations/2026_04_17_100003_create_offer_items_table.php b/database/migrations/2026_04_17_100003_create_offer_items_table.php new file mode 100644 index 0000000..c540a9b --- /dev/null +++ b/database/migrations/2026_04_17_100003_create_offer_items_table.php @@ -0,0 +1,68 @@ +id(); + + $t->foreignId('offer_version_id') + ->constrained('offer_versions') + ->cascadeOnDelete(); + + $t->unsignedInteger('position')->default(0); + + $t->enum('type', [ + 'travel', + 'service', + 'option', + 'discount', + 'insurance', + 'custom', + ]); + + $t->string('title'); + $t->text('description')->nullable(); + + $t->unsignedInteger('quantity')->default(1); + $t->decimal('price_per_unit', 10, 2)->default(0); + $t->decimal('total_price', 10, 2)->default(0); + + // Bewusst OHNE FK-Constraint — siehe Risiko R4 + $t->unsignedBigInteger('travel_program_id')->nullable(); + $t->unsignedBigInteger('fewo_lodging_id')->nullable(); + + $t->json('metadata')->nullable(); + + $t->timestamps(); + + $t->index(['offer_version_id', 'position']); + }); + } + + public function down(): void + { + Schema::dropIfExists('offer_items'); + } +}; diff --git a/database/migrations/2026_04_17_100004_create_offer_templates_table.php b/database/migrations/2026_04_17_100004_create_offer_templates_table.php new file mode 100644 index 0000000..67d4bab --- /dev/null +++ b/database/migrations/2026_04_17_100004_create_offer_templates_table.php @@ -0,0 +1,70 @@ +id(); + + // `branch` existiert schon im CRM — Vorlagen können so pro + // Filiale gepflegt werden. Eine spätere Erweiterung auf + // `organization_id` (Modul 5) erfolgt additiv. + $t->foreignId('branch_id') + ->nullable() + ->constrained('branch') + ->nullOnDelete(); + + $t->string('name'); + $t->text('description')->nullable(); + + $t->string('default_headline')->nullable(); + $t->text('default_intro')->nullable(); + $t->longText('default_itinerary')->nullable(); + $t->text('default_closing')->nullable(); + + // Array aus [{title, description, type, price_per_unit, quantity}, …] + $t->json('default_items')->nullable(); + + $t->boolean('is_active')->default(true); + $t->foreignId('created_by')->constrained('users'); + + $t->timestamps(); + $t->softDeletes(); + + $t->index(['branch_id', 'is_active']); + }); + + Schema::table('offer_versions', function (Blueprint $t) { + $t->foreign('template_id') + ->references('id') + ->on('offer_templates') + ->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('offer_versions', function (Blueprint $t) { + $t->dropForeign(['template_id']); + }); + + Schema::dropIfExists('offer_templates'); + } +}; diff --git a/database/migrations/2026_04_17_100005_create_offer_files_table.php b/database/migrations/2026_04_17_100005_create_offer_files_table.php new file mode 100644 index 0000000..2e8442f --- /dev/null +++ b/database/migrations/2026_04_17_100005_create_offer_files_table.php @@ -0,0 +1,48 @@ +id(); + + $t->foreignId('offer_version_id') + ->constrained('offer_versions') + ->cascadeOnDelete(); + + $t->string('identifier', 64)->nullable(); + $t->string('filename'); + $t->string('dir'); + $t->string('original_name'); + $t->string('ext', 16); + $t->string('mine', 128); + $t->unsignedBigInteger('size')->default(0); + + $t->boolean('include_in_pdf')->default(true); + + $t->timestamps(); + + $t->index(['offer_version_id', 'identifier']); + }); + } + + public function down(): void + { + Schema::dropIfExists('offer_files'); + } +}; diff --git a/database/migrations/2026_04_17_100006_create_offer_access_tokens_table.php b/database/migrations/2026_04_17_100006_create_offer_access_tokens_table.php new file mode 100644 index 0000000..cd10b9c --- /dev/null +++ b/database/migrations/2026_04_17_100006_create_offer_access_tokens_table.php @@ -0,0 +1,48 @@ +id(); + + $t->foreignId('offer_id') + ->constrained('offers') + ->cascadeOnDelete(); + + $t->foreignId('offer_version_id') + ->constrained('offer_versions') + ->cascadeOnDelete(); + + $t->string('token_hash', 64)->unique(); + $t->dateTime('expires_at')->nullable(); + $t->dateTime('first_opened_at')->nullable(); + $t->dateTime('revoked_at')->nullable(); + + $t->timestamps(); + + $t->index(['offer_id', 'revoked_at']); + }); + } + + public function down(): void + { + Schema::dropIfExists('offer_access_tokens'); + } +}; diff --git a/database/migrations/2026_04_17_100007_add_offer_refs_to_offers_and_bookings.php b/database/migrations/2026_04_17_100007_add_offer_refs_to_offers_and_bookings.php new file mode 100644 index 0000000..32681ab --- /dev/null +++ b/database/migrations/2026_04_17_100007_add_offer_refs_to_offers_and_bookings.php @@ -0,0 +1,57 @@ +foreign('current_version_id') + ->references('id') + ->on('offer_versions') + ->nullOnDelete(); + }); + + Schema::table('booking', function (Blueprint $t) { + // `inquiry_id` kommt aus Modul-3 Phase 2 (war vorher `lead_id`) + $t->unsignedBigInteger('offer_id')->nullable()->after('inquiry_id'); + + $t->foreign('offer_id') + ->references('id') + ->on('offers') + ->nullOnDelete(); + + $t->index('offer_id'); + }); + } + + public function down(): void + { + Schema::table('booking', function (Blueprint $t) { + $t->dropForeign(['offer_id']); + $t->dropIndex(['offer_id']); + $t->dropColumn('offer_id'); + }); + + Schema::table('offers', function (Blueprint $t) { + $t->dropForeign(['current_version_id']); + }); + } +}; diff --git a/dev/audit-april-2025.md b/dev/audit-april-2025.md new file mode 100644 index 0000000..90044c1 --- /dev/null +++ b/dev/audit-april-2025.md @@ -0,0 +1,242 @@ +# Code-Audit – mein.sterntours.de (April 2025) + +Durchgeführt nach Framework-Update auf Laravel 10 / PHPUnit 10. + +--- + +## Status-Übersicht + +| Bereich | Status | +|---------|--------| +| PHPUnit-Konfiguration | ✅ behoben | +| UserFactory | ✅ auf Laravel 10 migriert | +| Test-Abdeckung | ✅ Grundgerüst erstellt | +| Composer-Autoload | ✅ behoben | +| User/Customer/Lead/Booking HasFactory | ✅ hinzugefügt | +| Hardcoded API-Key | ✅ behoben (→ `config('app.success_key')`) | +| Code-Duplizierung Services | ✅ `MailDirService` extrahiert | +| BookingController Request-Facade | ✅ auf Injection umgestellt | +| Util.php Carbon-Calls | ✅ behoben | +| Leere Konstruktoren | ✅ entfernt | +| Factories Customer/Lead/Booking | ✅ erstellt | +| Veraltetes Frontend-Tooling | ℹ️ bekannt – separates Vorhaben | + +--- + +## Behobene Probleme + +### 1. `phpunit.xml` — PHPUnit 10 Inkompatibilität + +**Datei:** `phpunit.xml` + +PHPUnit 10 hat mehrere Attribute aus dem XML entfernt, die vorher vorhanden waren. Das führte zu Warnings bzw. Fehlern beim Testlauf. + +**Entfernt:** +- `backupStaticAttributes` (seit PHPUnit 9.6 deprecated, in 10 entfernt) +- `convertErrorsToExceptions`, `convertNoticesToExceptions`, `convertWarningsToExceptions` (in PHPUnit 10 entfernt) +- `processIsolation`, `stopOnFailure` (in PHPUnit 10 aus XML entfernt → nur noch als CLI-Flag) + +**Geändert:** +- `` → `` (PHPUnit 10 Syntax) + +--- + +### 2. `database/factories/UserFactory.php` — Laravel 7 Syntax + +**Datei:** `database/factories/UserFactory.php` + +Die alte Factory nutzte die Laravel 7 Closure-Syntax (`$factory->define()`), die in Laravel 10 nicht mehr unterstützt wird. + +**Vorher:** +```php +$factory->define(App\User::class, function (Faker $faker) { ... }); +``` + +**Nachher:** Vollständige Laravel 10 `Factory`-Klasse mit States (`admin()`, `inactive()`). + +--- + +### 3. `composer.json` — Veraltetes Classmap-Autoloading + +**Datei:** `composer.json` + +```json +// Vorher (Laravel 7 Stil): +"classmap": ["database/seeds", "database/factories"] + +// Nachher (PSR-4): +"Database\\Factories\\": "database/factories/", +"Database\\Seeders\\": "database/seeds/" +``` + +--- + +### 4. `app/User.php` — HasFactory-Trait fehlte + ungültiger Import + +`HasFactory` wurde hinzugefügt, damit `User::factory()` in Tests funktioniert. + +Der ungültige Import `PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions\F` (nie benutzt) wurde entfernt. + +--- + +## Offene Probleme (Handlungsbedarf) + +### ⚠️ Hardcoded API-Key (Priorität: HOCH) + +**Datei:** `app/Http/Controllers/API/BookingController.php:12` + +```php +private $successKey = 'f6077389c9ce710e554763a5de02c8ec'; +``` + +Der API-Key ist direkt im Source Code und damit im Git-Repository. Das ist ein Sicherheitsrisiko. + +**Lösung:** +```php +// .env +BOOKING_IMPORT_KEY=f6077389c9ce710e554763a5de02c8ec + +// Controller +private string $successKey; + +public function __construct() +{ + $this->successKey = config('app.booking_import_key'); +} +``` + +Dazu in `config/app.php` ergänzen: +```php +'booking_import_key' => env('BOOKING_IMPORT_KEY'), +``` + +--- + +### ⚠️ Code-Duplizierung: `Services/Booking.php` und `Services/Lead.php` + +Beide Services enthalten nahezu identische Methoden: +- `getCustomerMailDirs()` +- `getCustomerMailDir($id)` +- `getCustomerMailName($dir, $id)` +- `getCustomerMailEmails($dir, $id)` +- `setOutputDirs($dir, $subdir)` +- `getMailDirNotInOutput($id, $dir)` + +**Lösung:** Einen gemeinsamen `MailDirService` (oder Trait) extrahieren. + +--- + +### ⚠️ `BookingController` nutzt Request-Facade statt Dependency Injection + +**Datei:** `app/Http/Controllers/BookingController.php:5` + +```php +use Request; // Facade +// ... +$data = Request::all(); +``` + +**Lösung:** `Illuminate\Http\Request $request` als Parameter injizieren. + +--- + +### ⚠️ `Services/Util.php` — veraltete Carbon-Nutzung + +**Datei:** `app/Services/Util.php:54-58` + +```php +\Carbon::parse($date) // veraltet +\Util::formatDateDB() // statischer Aufruf über Facade-Alias +``` + +**Lösung:** `Carbon\Carbon::parse($date)` und `self::formatDateDB()` verwenden. + +--- + +### ℹ️ `API/BookingController` — leerer Konstruktor + +**Datei:** `app/Http/Controllers/API/BookingController.php:17-19` + +```php +public function __construct() +{ +} +``` + +Leere Konstruktoren ohne Parameter sollen laut Projekt-Konvention entfernt werden. + +--- + +### ℹ️ Frontend-Tooling — stark veraltet + +**Datei:** `package.json` + +| Package | Aktuell | Stand | +|---------|---------|-------| +| `laravel-mix` | 2.1.11 | 2018 — aktuell: 6.x | +| `cross-env` | 5.1.4 | veraltet | +| `node-sass` | ~4.7.2 | deprecated → `sass` (dart-sass) verwenden | +| `bootstrap` | ~4.1.1 | Bootstrap 5.x verfügbar | + +Ein Frontend-Update ist ein größeres Vorhaben und sollte separat geplant werden. Das Build-System funktioniert noch, ist aber auf veralteten Node-Versionen abhängig. + +--- + +### ℹ️ `BaseRepository.php` — fehlende Return-Types + +**Datei:** `app/Repositories/BaseRepository.php` + +Alle Methoden haben keine Return-Type-Deklarationen. Als technische Schuld für künftige Refactoring-Sessions notiert. + +--- + +### ℹ️ `phpunit.xml` — SQLite & zweite Datenbankverbindung + +Tests nutzen SQLite (`:memory:`), die `mysql_stern`-Verbindung ist nicht konfiguriert. Models, die `mysql_stern` nutzen (`App\Models\Sym\*`, `App\Models\TravelBooking`), können in Tests nicht ohne Mock/Skip getestet werden. + +**Empfehlung:** Für Tests eine zweite SQLite-Verbindung in `phpunit.xml` konfigurieren: +```xml + + +``` + +--- + +## Neue Test-Dateien + +| Datei | Typ | Was wird getestet | +|-------|-----|-------------------| +| `tests/Unit/Services/UtilTest.php` | Unit | `Util`-Service: Formatierung, Sanitizing, Placeholder-Ersetzung | +| `tests/Feature/Auth/LoginTest.php` | Feature | Login, Logout, Redirect für Gäste/inaktive User | +| `tests/Feature/Api/BookingImportTest.php` | Feature | API-Key-Validierung, 404-Handling | +| `tests/Feature/BookingControllerTest.php` | Feature | Middleware (Guest-Redirect, Admin-Check) | + +### Tests ausführen + +```bash +# Alle Tests +php artisan test --compact + +# Nur Unit-Tests +php artisan test --compact tests/Unit + +# Nur die neuen Feature-Tests +php artisan test --compact tests/Feature/Auth/LoginTest.php +php artisan test --compact tests/Feature/Api/BookingImportTest.php +php artisan test --compact tests/Feature/BookingControllerTest.php + +# Einzelner Test per Filter +php artisan test --compact --filter=testLoginFailsWithWrongPassword +``` + +--- + +## Nächste Schritte (Empfehlung) + +1. **API-Key** aus Code entfernen → `.env`-Variable (HOCH) +2. `MailDirService` extrahieren (Booking + Lead Services zusammenführen) +3. `BookingController` auf Request-Injection umstellen +4. `Util.php` Carbon-Calls bereinigen +5. Leere Konstruktoren entfernen +6. Weitere Factories erstellen (`Customer`, `Booking`, `Lead`) für tiefere Test-Abdeckung +7. Frontend-Tooling-Update planen (laravel-mix → Vite oder mix v6) diff --git a/dev/briefings.md b/dev/briefings.md new file mode 100644 index 0000000..43729fb --- /dev/null +++ b/dev/briefings.md @@ -0,0 +1,61 @@ +Anfrage direkt vom Kunden + +"Zudem habe ich ein wichtiges Anliegen. Wir möchten aus unserem System unbedingt einfach, schnell und qualitativ hochwertig Angebote erstellen können, die wir ja individuell ausarbeiten. Es ist also fast jedes der Angebote anders. + +Derzeit bestehen unsere Angebote aus Texten in einer einfachen Email. Das finde ich unprofessionell, denn die Kunden haben damit nichts, was sie sich schön ausdrucken können, was übersichtlich wie ein vernünftiges Angebot aussieht. Wenn es dabei auch noch um sehr teure Reisen geht, passt das einfach nicht und zeugt nicht von Seriosität. + +Daher möchte ich fragen, ob wir daran arbeiten können? + +Theoretisch kann ein solches Angebot aussehen wie unser Buchungsauftrag, nur dass "Angebot" darüber steht (oder Persönliches Angebot für Frau XXX). Wichtig ist uns , dass es einfach zu erstellen ist und man ggf. bei einer sehr hochwertigen Reise auch weiterführenden Text zum Reiseverlauf für die Reisebeschreibung hinzufügen kann. Das kann aber ggf. auch auf einem separaten Briefbogen erfolgen, den wir dann hochladen. + +Aktuell gehen wir so vor: Kunde ruft an, wir füllen unser Kontaktformular mit seinen Anfragedaten aus, sodass ein Kundendatensatz und eine Anfrage im System entsteht. Dann schreiben wir Emails hin und her, bis er sich für eine Buchung entscheidet und dann füllen wir das Buchungsformular aus. Daraus entsteht dann ein zweites mal der Kundendatensatz und der Buchungssatz. + +An der Stelle, an der wie die Anfrage erstellen, möchten wir im System bereits die Möglichkeit haben, das Angebot zu erstellen. Vielleicht kann man hier aus einer der Vorlagen eine Organisation laden, dieses dann an die individuelle Anfrage anpassen und daraus dann das Angebot als Pdf erstellen. Anschließend wäre es gut, wenn hieraus direkt eine Buchung generiert werden könnte, denn dann haben wir ja schon alle Leistungen im System eingetragen." + + + +Weitere wichtige Punkte, die in den Entwicklungsplan gehören: + +Das System muss grundsätzlich an vielen Stellen modernisiert werden und optimiert werden, auch was Darstellung und Benutzer Bedienung angeht. + +https://mein.sterntours.test/requests +Ein wichtiger Punkt sind die Buchungen hier muss noch deutlich optimiert werden. + +Derzeit kommen alle Buchungen über die Webseite Siehe /sterntours.de/src/AppBundle/Controller /sterntours.de/src/AppBundle/Export +Es muss auch möglich sein, direkt im System Buchungen anzulegen. Hintergrund ist wenn derzeit eine Anfrage per Telefon kommt, gehen die Mitarbeiter tatsächlich auf die Webseite und lösen eine Bestellung aus, Das muss auch einfach im System funktionieren + +Unter dem Punkt Buchungen muss Der Reiter organisation verbessert werden Es muss von der Benutzerführung einfacher und übersichtlicher werden, eine Organisation einer Reise anzulegen und zu verwalten. + +Unter dem Punkt Buchungen gibt es E-Mails wird eine E-Mail geschrieben. Soll diese sofort per AutoSave als Entwurf gespeichert werden. + +Zusätzlich gilt das, was hier noch aufgeschlüsselt ist mein.sterntours.de/dev/customer-bookings/* + +Unter dem Punkt Fewos +https://mein.sterntours.test/travel_user_booking_fewos + +Auch hier müssen die E-Mails, die den selben Prinzip die Buchung verfolgen auch ein Auto Safe bekommen. +Da die E-Mails im System grundsätzlich eine einheitliche Programmierung haben, wäre es sinnvoll, diese Skripte zu migrieren. + +Bei den Buchungen der Ferienwohnungen tauchen immer wieder Fehler auf bei der Belegung das liegt daran, wenn die Daten geändert werden. Diese werden nicht synchron überschrieben, so dass Doppelbuchungen teilweise im System vorliegen. Ich glaube nur in der Datenbank das muss natürlich bereinigt werden. + +Zusätzlich gibt es ein weiteres Backend sterntours.de/src/AppBundle/Controller/AdminController.php Dieses muss auch sauber in das Hauptbackend mein.sterntours.de (v3) Übernommen werden, damit wir das alte abstellen können. Damit wird sich dann vermutlich auch der Fehler erledigen. Grundsätzlich darf gerne die Datenstruktur migriert werden und optimiert werden. + +Hier gibt es schon mal einen Ansatz für die Darstellung des Navigations,dieser ist aber noch nicht gut +https://mein.sterntours.test/navigation-api + +Im System gerade in Fronten sterntours.de/src/AppBundle/Listener/KernelControllerListener.php Ist die Struktur der Navigation und der des Seitenbaums sehr eigenwillig und muss verbessert und optimiert werden mein.sterntours.de/app/Models/Page.php Alles liegt in dieser Page Tabelle und ist sehr schlecht. War bei undurchschaubar hier muss ein deutlich Cleaneres konzept her. So dass auch hier https://mein.sterntours.test/navigation-api Übersichtlich ein Navigations Baum erstellt werden kann, der deutlich klarer zeigt, was auf dem Fronten zu sehen ist und wo ich welche Elemente ändern kann. + +Teilweise gibt es schon CMS Ansätze, die die Fronten Zeiten bearbeitBar machen. Dieses ist hier in Kategorien geteilt. +https://mein.sterntours.test/cms/feedback +https://mein.sterntours.test/cms/fewo/content +https://mein.sterntours.test/cms/travel_guide/content +https://mein.sterntours.test/iq/content/tree/index +https://mein.sterntours.test/cms/news +https://mein.sterntours.test/cms/answer_question +https://mein.sterntours.test/cms/sidebar +https://mein.sterntours.test/cms/content/infos +https://mein.sterntours.test/cms/content/all + +Grundsätzlich muss das alles mehr vereinheitlicht werden und deutlicher werden, was, wo geändert werden kann. Die Einzelmodule können so bleiben. Es muss nur klarer in der Benutzerführung werden und auch aus der Hauptnavigation erreichbar sein und deutlich sein, wo was hingehört. + +Jetzt kommt noch ein sehr großer Punkt und zwar gibt es noch ein Verwaltung v2.stern-tours.de/application/controllers/acp Hier werden Reisen angelegt und auch die Reisezeiträume verwaltet. Dieses ist mittlerweile absolut veraltet und auch Fehler anfällig. Ein großes neues Modul wird es sein dieses auf die bestehende mein.sterntours.de (v3) Zu migrieren d.h. der komplette Funktionsumfang muss in das Back and ein entwickelt werden und benutzerfreundliche gemacht werden sowie müssen die gesamten Skripte etc. deutlich optimiert werden. diff --git a/dev/customer-bookings/konzept.md b/dev/customer-bookings/konzept.md new file mode 100644 index 0000000..cb48916 --- /dev/null +++ b/dev/customer-bookings/konzept.md @@ -0,0 +1,441 @@ +# Konzept: Neustrukturierung Customer / Lead / Booking + +**Status:** Entwurf +**Erstellt:** April 2025 +**Ziel:** Beseitigung der Daten-Redundanz und Vereinfachung der Kernstruktur + +--- + +## 1. Ist-Zustand — Analyse der Probleme + +### 1.1 Aktuelle Datenfluss-Kette + +``` +Anfrage (Lead) + └── customer_id → Customer (neu angelegt pro Lead!) + └── LeadMail + └── LeadFile + └── LeadNotice + └── LeadParticipant + | + | createBooking() + ↓ +Buchung (Booking) + └── customer_id → Customer (derselbe, aber Daten werden separat gepflegt) + └── lead_id → Lead (Rückreferenz) + └── CustomerMail + └── BookingFile + └── BookingNotice + └── Participant (Kopie von LeadParticipant!) +``` + +### 1.2 Konkrete Probleme + +| Problem | Auswirkung | +|---------|-----------| +| **Kunde wird pro Anfrage neu angelegt** | Bucht ein Kunde zweimal: 2 Customer-Datensätze, keine Kundenhistorie | +| **Teilnehmer doppelt gespeichert** | `participant_name/firstname/birthdate` in `lead`, `booking` UND `lead_participant` + `participant` | +| **Mail-Tabellen aufgesplittet** | `lead_mail` für Anfragen, `customer_mail` für Buchungen — gleiche Struktur, getrennte Tabellen | +| **Notizen aufgesplittet** | `lead_notice` und `booking_notice` — identisch, getrennt | +| **Datei-Tabellen aufgesplittet** | `lead_file`, `booking_file`, `customer_fewo_file` — gleiche Funktion | +| **Kein übergreifendes Kunden-Profil** | Alle Buchungen/Anfragen eines Kunden nur per ID-Lookup findbar, aber nie wirklich verknüpft | +| **`createBooking()` kopiert Daten** | `LeadRepository::createBooking()` kopiert Participants 1:1 — spätere Änderungen laufen auseinander | + +--- + +## 2. Soll-Zustand — Zielbild + +### 2.1 Kernprinzip + +``` +Contact (Stammkunde — einmal, für immer) + └── hasMany: Inquiry (früher: Lead) + └── hasMany: Booking + +Inquiry (Anfrage) + └── belongsTo: Contact + └── hasMany: Booking (eine Anfrage kann zu einer Buchung werden) + └── hasMany: Communication (Mails, Notizen, Dateien) + +Booking (Buchung) + └── belongsTo: Contact + └── belongsTo: Inquiry + └── hasMany: Communication + └── hasMany: Participant +``` + +### 2.2 Neue Tabellen-Struktur + +#### `contacts` (ersetzt `customer`) +Identisch zur `customer`-Tabelle — nur umbenannt und mit Deduplizierungslogik ausgestattet. + +```sql +CREATE TABLE contacts ( + -- alle bisherigen Felder aus customer + id, salutation_id, title, name, firstname, birthdate, + company, street, zip, city, email, phone, phonebusiness, + phonemobile, fax, bank, bank_code, bank_account_number, + credit_card_type_id, credit_card_number, + credit_card_expiration_date, participants_remarks, + miscellaneous_remarks, country_id, + -- neu: + merged_into_id INT NULL, -- zeigt auf Haupt-Datensatz bei Duplikaten + created_at, updated_at, deleted_at +); +``` + +#### `inquiries` (umbenennung von `lead`) +Weitgehend identisch, `customer_id` → `contact_id`. + +```sql +-- Änderungen gegenüber lead: +ALTER TABLE inquiries + RENAME COLUMN customer_id TO contact_id; +-- Teilnehmerfelder bleiben vorerst (Abwärtskompatibilität Phase 1) +-- Entfernung in Phase 3 +``` + +#### `bookings` (geringfügige Anpassung) +`customer_id` → `contact_id`, `lead_id` → `inquiry_id`. + +```sql +-- inquiry_id bleibt nullable: +-- Direktbuchungen (ohne vorherige Anfrage) sollen möglich sein → inquiry_id = NULL +-- Teilnehmerfelder (participant_name etc.) fallen in Phase 3 weg +-- → wandern komplett in die participants-Tabelle +``` + +#### `participants` (konsolidiert `lead_participant` + `participant`) + +```sql +CREATE TABLE participants ( + id, + contact_id INT NULL, -- direkte Zuordnung zum Stammkunden (optional) + inquiry_id INT NULL, -- FK auf inquiries (früher lead_participant) + booking_id INT NULL, -- FK auf bookings (früher participant) + salutation_id INT NULL, + name VARCHAR, + firstname VARCHAR, + birthdate DATE NULL, + participant_child BOOL DEFAULT FALSE, + nationality_id INT NULL, + is_lead_contact BOOL DEFAULT FALSE, -- markiert den Hauptreisenden + created_at, updated_at +); +``` + +#### `communications` (konsolidiert `lead_mail` + `customer_mail`) + +```sql +CREATE TABLE communications ( + id, + contact_id INT NULL, + inquiry_id INT NULL, + booking_id INT NULL, + -- alle bisherigen Felder aus lead_mail/customer_mail: + from_email, to_email, from_name, to_name, + subject, body, sent_at, + dir, subdir, + -- Typ-Unterscheidung: + context ENUM('inquiry', 'booking') NOT NULL, + created_at, updated_at +); +``` + +#### `notices` (konsolidiert `lead_notice` + `booking_notice`) + +```sql +CREATE TABLE notices ( + id, + inquiry_id INT NULL, + booking_id INT NULL, + from_user_id INT NOT NULL, + to_user_id INT NULL, + message TEXT, + edit_at DATETIME NULL, + created_at, updated_at +); +``` + +#### `attachments` (konsolidiert `lead_file` + `booking_file` + `customer_fewo_file`) + +```sql +CREATE TABLE attachments ( + id, + contact_id INT NULL, + inquiry_id INT NULL, + booking_id INT NULL, + disk, path, filename, mime_type, filesize, + identifier VARCHAR NULL, + created_at, updated_at +); +``` + +--- + +## 3. Migrationsstrategie — Phasen + +Die Migration ist bewusst **rückwärtskompatibel** geplant: jede Phase kann unabhängig deployed werden, das System bleibt zu jeder Zeit funktionsfähig. + +--- + +### Phase 1 — Contact-Deduplizierung (Kern-Problem lösen) + +**Ziel:** Mehrfach-Kunden zusammenführen. Kein Datenverlust. + +#### 1a) Duplikate identifizieren + +```php +// artisan-Befehl: php artisan contacts:find-duplicates +// Gruppierungsstrategie (absteigend nach Konfidenz): +// 1. Gleiche E-Mail-Adresse → sicher identisch +// 2. Gleicher Name + Vorname + Geburtsdatum → sehr wahrscheinlich identisch +// 3. Gleicher Name + Vorname + PLZ → wahrscheinlich identisch (manuell prüfen) +``` + +Ausgabe: CSV-Liste mit Gruppen und vorgeschlagenem Haupt-Datensatz. + +#### 1b) `merged_into_id` Spalte hinzufügen + +```sql +ALTER TABLE customer ADD COLUMN merged_into_id INT NULL; +ALTER TABLE customer ADD COLUMN merged_at DATETIME NULL; +``` + +#### 1c) Migration ausführen + +```php +// artisan-Befehl: php artisan contacts:merge-duplicates --dry-run +// Dann: php artisan contacts:merge-duplicates --confirm +// +// Für jeden gefundenen Duplikat-Cluster: +// 1. Neuesten Datensatz (höchste ID / neuestes updated_at) als Master wählen +// → Entscheidung: immer die neueste Adresse gewinnt +// 2. Alle leads: customer_id → master_id +// 3. Alle bookings: customer_id → master_id +// 4. Duplikat: merged_into_id = master_id setzen +``` + +#### 1d) Customer-Model: Abfragen abfangen + +```php +// app/Models/Customer.php — GlobalScope hinzufügen +protected static function booted(): void +{ + static::addGlobalScope('not_merged', function ($query) { + $query->whereNull('merged_into_id'); + }); +} +``` + +**Aufwand:** ~2 Tage +**Risiko:** Gering — nur Lese-/Schreib-Operationen, keine Struktur-Änderung + +--- + +### Phase 2 — Tabellen umbenennen (Customer → Contact, Lead → Inquiry) + +**Ziel:** Sprechende Namen einführen, interne Logik bereinigen. + +```sql +-- Migration: +RENAME TABLE customer TO contacts; +RENAME TABLE lead TO inquiries; + +-- Booking: +ALTER TABLE bookings RENAME COLUMN lead_id TO inquiry_id; +-- customer_id bleibt vorerst, wird in Phase 2b auf contact_id umgestellt +``` + +**Code-Änderungen:** +- `App\Models\Customer` → bleibt, aber `protected $table = 'contacts'` +- `App\Models\Lead` → bleibt, aber `protected $table = 'inquiries'` +- Alle Referenzen auf `lead_id` in Booking → `inquiry_id` +- Controller-Routen: `/lead/` → `/inquiry/` (alte URLs: Redirect 301) + +**Aufwand:** ~3 Tage +**Risiko:** Mittel — viele Stellen im Code müssen angepasst werden. Sorgfältige Suche mit `grep -r "lead_id\|customer_id\|'lead'\|'customer'"`. + +--- + +### Phase 3 — Participants konsolidieren + +**Ziel:** `lead_participant` und `participant` zu einer Tabelle zusammenführen. Teilnehmerfelder in `lead` und `booking` entfernen. + +```sql +CREATE TABLE participants ( + -- wie in 2.2 beschrieben +); + +-- Daten migrieren: +INSERT INTO participants (inquiry_id, name, firstname, birthdate, ...) + SELECT lead_id, participant_name, participant_firstname, participant_birthdate, ... + FROM lead_participant; + +INSERT INTO participants (booking_id, name, firstname, birthdate, ...) + SELECT booking_id, participant_name, participant_firstname, participant_birthdate, ... + FROM participant; + +-- Hauptreisenden aus den denormalisierten Feldern migrieren: +-- (lead.participant_name → participant mit is_lead_contact = true) +INSERT INTO participants (inquiry_id, name, firstname, birthdate, is_lead_contact, ...) + SELECT id, participant_name, participant_firstname, participant_birthdate, true, ... + FROM lead + WHERE participant_name IS NOT NULL; +``` + +**Code-Änderungen:** +- `LeadRepository::createBooking()` — kein Kopieren von Participants mehr nötig; bestehende `inquiry_id`-Participants werden automatisch per `booking_id` ergänzt +- `lead_participant`, `participant` Tabellen nach Abgleich droppen +- Denormalisierte Felder (`participant_name` etc.) in `lead` und `booking` mit `NULL` befüllen, dann in Phase 4 entfernen + +**Aufwand:** ~4 Tage +**Risiko:** Mittel — betrifft PDF-Generierung, Buchungsbestätigungen; diese müssen angepasst werden + +--- + +### Phase 4 — Communications / Notices / Attachments konsolidieren + +**Ziel:** Die 6+ Mail/Notiz/Datei-Tabellen auf 3 gemeinsame Tabellen reduzieren. + +```sql +-- Mails migrieren: +INSERT INTO communications (inquiry_id, context, from_email, ...) + SELECT lead_id, 'inquiry', from_email, ... FROM lead_mail; + +INSERT INTO communications (booking_id, context, from_email, ...) + SELECT booking_id, 'booking', from_email, ... FROM customer_mail; + +-- Notizen migrieren: +INSERT INTO notices (inquiry_id, from_user_id, message, ...) + SELECT lead_id, from_user_id, message, ... FROM lead_notice; + +INSERT INTO notices (booking_id, from_user_id, message, ...) + SELECT booking_id, from_user_id, message, ... FROM booking_notice; + +-- Dateien migrieren: +INSERT INTO attachments (inquiry_id, disk, path, ...) + SELECT lead_id, disk, path, ... FROM lead_file; + +INSERT INTO attachments (booking_id, disk, path, ...) + SELECT booking_id, disk, path, ... FROM booking_file; +``` + +**Code-Änderungen:** +- `LeadMailRepository`, `CustomerMailRepository` → gemeinsames `CommunicationRepository` +- `LeadFileRepository`, `BookingFileRepository` → gemeinsames `AttachmentRepository` +- `MailDirService` (bereits vorhanden) erhält einheitlichen Datenzugriff +- Views für Mails/Notizen/Dateien in Lead und Booking können shared werden + +**Aufwand:** ~5 Tage +**Risiko:** Hoch — betrifft viele Views und Controller. Muss sehr sorgfältig getestet werden. + +--- + +## 4. Neues Beziehungsmodell (Zielbild) + +``` +Contact (1) + ├── (N) Inquiry + │ ├── (N) Participant [inquiry_id] + │ ├── (N) Communication [inquiry_id] + │ ├── (N) Notice [inquiry_id] + │ ├── (N) Attachment [inquiry_id] + │ └── (1) Booking + │ ├── (N) Participant [booking_id] + │ ├── (N) Communication [booking_id] + │ ├── (N) Notice [booking_id] + │ ├── (N) Attachment [booking_id] + │ └── (N) BookingDocument, BookingInvoice, ... + └── (N) Booking (direkt, ohne Umweg über Inquiry) +``` + +--- + +## 5. Controller-Vereinfachung + +Nach den Migrationen können Controller und Views schrittweise zusammengelegt werden: + +| Jetzt | Ziel | +|-------|------| +| `LeadController` | `InquiryController` | +| `CustomerController` | `ContactController` | +| `BookingController` | bleibt, aber schlanker | +| `LeadMailController` + `CustomerMailController` | `CommunicationController` | +| `CustomerFewoMailController` | → `CommunicationController` (mit Fewo-Kontext) | + +Die Detail-Seiten für Lead und Booking sind strukturell fast identisch. Langfristig könnte es eine gemeinsame Basis-View geben, die je nach Kontext unterschiedliche Sektionen einblendet. + +--- + +## 6. Empfohlene Reihenfolge + +``` +Phase 1 (Contact-Deduplizierung) + → Unmittelbarer Gewinn: Kundenhistorie ist korrekt + → Unabhängig von allen anderen Phasen + → Sofort umsetzbar + +Phase 2 (Umbenennung) + → Voraussetzung für Phase 3 & 4 + → Kann parallel zur normalen Entwicklung erfolgen + +Phase 3 (Participants) + → Abhängig von Phase 2 + → Direkte Auswirkung auf PDF-Generierung — sorgfältig testen + +Phase 4 (Communications/Notices/Attachments) + → Größtes Vorhaben, letzter Schritt + → Kann in Teilschritte aufgesplittet werden + (erst Notices, dann Attachments, dann Communications) +``` + +--- + +## 7. Was NICHT geändert werden soll + +- **Buchungs-Detailseite** (`booking.detail`): Bleibt strukturell erhalten — zu komplex für gleichzeitige Überarbeitung +- **PDF-Generierung**: Erst nach Phase 3 anpassen (Participants-Konsolidierung) +- **Fewo-Buchungsstruktur** (`FewoLodging`, `TravelUserBookingFewo`): Separates Thema, nicht Teil dieses Konzepts +- **Legacy-Datenbank** (`mysql_stern`): Keine Änderungen — bleibt read-only + +--- + +## 8. Entscheidungen (geklärt) + +| # | Frage | Entscheidung | +|---|-------|-------------| +| 1 | Welcher Datensatz wird Master bei Duplikaten mit verschiedenen Adressen? | **Immer der neueste** (höchste ID / jüngstes `updated_at`) — vollautomatisch, kein manueller Review | +| 2 | Direktbuchung ohne vorherige Anfrage? | **Ja** — `inquiry_id` in `bookings` bleibt `NULL`able; Direktbuchungen sind vorgesehen | +| 3 | Was bedeutet `is_rebook`? | **Umbuchung** (nicht Wiederbuchung). Checkbox „Umbuchung abgeschlossen" auf der Anfrage. Feld bleibt auf `inquiries`, keine Sonderbehandlung nötig | +| 4 | DSGVO — Einwilligungen pro Datensatz? | **Kein Problem.** Einwilligung war immer Pflichtfeld beim Formular-Submit. Jede Anfrage enthält implizit eine gültige Einwilligung — keine separate Migrationsprüfung nötig | + +### Auswirkungen auf die Migration + +**Zu 1 — Automatische Master-Wahl:** +```php +// In contacts:merge-duplicates: +// Master = Customer::where('email', $email)->orderByDesc('updated_at')->first() +// Kein manuelles Eingreifen erforderlich +``` + +**Zu 2 — Direktbuchung:** +```sql +-- bookings.inquiry_id bleibt NULL erlaubt (bereits so geplant) +-- Neuer Einstiegspunkt im UI: "Buchung direkt anlegen" ohne Lead-Voraussetzung +-- BookingController::store() darf inquiry_id weglassen +``` + +**Zu 3 — is_rebook bleibt auf Inquiry:** +```php +// Bedeutung: diese Anfrage ist eine Umbuchung einer bestehenden Buchung +// is_rebook: bool — Checkbox "Umbuchung abgeschlossen" +// Keine Änderung an der Logik, nur Umbenennung mit Phase 2 +``` + +**Zu 4 — DSGVO:** +``` +Keine zusätzlichen Schritte nötig. +Beim Mergen zweier Kontakte gilt: beide haben gültige Einwilligungen erteilt. +Der zusammengeführte Master-Datensatz ist datenschutzrechtlich unbedenklich. +``` diff --git a/dev/customer-bookings/umsetzung.md b/dev/customer-bookings/umsetzung.md new file mode 100644 index 0000000..eccfa6d --- /dev/null +++ b/dev/customer-bookings/umsetzung.md @@ -0,0 +1,464 @@ +# Umsetzung: Neustrukturierung Customer / Lead / Booking + +**Status:** Phase 1 auf Testsystem abgeschlossen — Contacts-Modul live; **Phase-2-App-Code vorbereitet (deploy-bereit)** +**Erstellt:** April 2025 +**Konzept:** [konzept.md](konzept.md) + +--- + +## Aktueller Fortschritt + +| Phase | Status | Deployed auf Test? | Deployed auf Live? | +|-------|--------|-------------------|-------------------| +| Phase 1 — Contact-Deduplizierung | ✅ Abgeschlossen | ✅ Ja | ⬜ Nein | +| Phase 1 — Contacts-Modul (neuer Code) | ✅ Abgeschlossen | ✅ Ja | ⬜ Nein | +| Phase 2 — App-Code (Models/Repos/Controller/Views auf `contacts`/`inquiries`/`inquiry_id`) | ✅ Abgeschlossen | ⬜ Nein | ⬜ Nein | +| Phase 2 — Tabellen-Migrationen (3 Migrationsdateien) | ⬜ Ausstehend (Code ist deploy-ready) | ⬜ Nein | ⬜ Nein | +| Phase 3 — Participants konsolidieren | ⬜ Ausstehend | ⬜ Nein | ⬜ Nein | +| Phase 4 — Communications / Notices / Attachments | ⬜ Ausstehend | ⬜ Nein | ⬜ Nein | + +### Was in Phase 1 umgesetzt wurde + +**Datenbank-Migrationen (alle auf Testsystem eingespielt):** +- `merged_into_id` + `merged_at` auf `customer`-Tabelle → Duplikat-Tracking +- `deleted_at` auf `customer`-Tabelle → Soft Delete für neue Contacts-UI + +**Artisan Commands:** +- `contacts:find-duplicates` — findet Duplikate nach E-Mail / Name+Geburtsdatum / Name+PLZ +- `contacts:merge-duplicates` — führt Duplikate zusammen (dry-run-Modus vorhanden) + +**Contacts-Modul (neuer paralleler Code, alter Code bleibt unberührt):** +- `app/Models/Contact.php` — Global Scope schließt Duplikate + gelöschte Kontakte aus +- `app/Repositories/ContactRepository.php` +- `app/Http/Controllers/ContactController.php` — index, detail, store, destroy, getContacts +- `resources/views/contact/` — index, detail, partials +- Neue Routes unter `/contacts` und `/contact/*` +- Neuer Navigationspunkt "Kontakte" im Sidenav + +**Contacts-Übersicht — Features:** +- DataTable mit Schnellsuche (Name, Vorname, E-Mail, Telefon gleichzeitig) +- PLZ/Ort-Suche mit OR-Logik über beide Felder +- Schnellfilter: Alle / Mit Anfragen / Mit Buchungen +- Anfragen- und Buchungs-Zähler pro Kontakt als klickbare Badges +- History-Modal: Klick auf Anfragen- oder Buchungs-Badge öffnet Modal mit vollständiger Verlaufsübersicht (AJAX, `_detail_history.blade.php` mit `$modal=true`, Links öffnen in neuem Tab) +- Löschen mit Bootstrap-Bestätigungs-Modal + Fehler-Toast (blockiert wenn Anfragen/Buchungen vorhanden) + +--- + +## Übersicht der Migrations-Dateien + +Alle Migrations-Dateien liegen in `database/migrations/` und können gezielt auf den Live-Server eingespielt werden. Jede Phase ist **unabhängig deploybar** — das System bleibt nach jeder Phase voll funktionsfähig. + +| Datei | Phase | Beschreibung | +|-------|-------|-------------| +| `2025_04_15_100001_phase1_add_merge_fields_to_customer_table.php` | 1 | `merged_into_id` + `merged_at` zu `customer` hinzufügen | +| `2025_04_15_100002_phase1_add_soft_delete_to_customer_table.php` | 1 | `deleted_at` (Soft Delete) zu `customer` hinzufügen | +| `2025_04_15_200001_phase2_rename_customer_to_contacts.php` | 2 | `customer` → `contacts` umbenennen | +| `2025_04_15_200002_phase2_rename_lead_to_inquiries.php` | 2 | `lead` → `inquiries` umbenennen | +| `2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php` | 2 | `booking.lead_id` → `booking.inquiry_id` | +| `2025_04_15_300001_phase3_create_participants_unified_table.php` | 3 | `participants_unified` erstellen + Daten migrieren | +| `2025_04_15_300002_phase3_drop_old_participant_tables.php` | 3 | `lead_participant` + `participant` droppen (**nach Test!**) | +| `2025_04_15_400001_phase4_create_communications_table.php` | 4 | `communications` erstellen + Daten migrieren | +| `2025_04_15_400002_phase4_create_notices_table.php` | 4 | `notices` erstellen + Daten migrieren | +| `2025_04_15_400003_phase4_create_attachments_table.php` | 4 | `attachments` erstellen + Daten migrieren | +| `2025_04_15_400004_phase4_drop_old_communication_tables.php` | 4 | `lead_mails` + `customer_mails` droppen (**nach Test!**) | +| `2025_04_15_400005_phase4_drop_old_notice_tables.php` | 4 | `lead_notices` + `booking_notices` droppen (**nach Test!**) | +| `2025_04_15_400006_phase4_drop_old_attachment_tables.php` | 4 | `lead_files` + `booking_files` droppen (**nach Test!**) | + +--- + +## Voraussetzungen + +```bash +# Auf dem Live-Server ausführen: +# 1. Datenbank-Backup erstellen (vor JEDER Phase!) +mysqldump -u root -ppassword stern_crm > backup_$(date +%Y%m%d_%H%M%S).sql + +# 2. Migration-Status prüfen +php artisan migrate:status +``` + +--- + +## Phase 1 — Contact-Deduplizierung + +### Schritt 1: Migration einspielen +```bash +php artisan migrate --path=database/migrations/2025_04_15_100001_phase1_add_merge_fields_to_customer_table.php +php artisan migrate --path=database/migrations/2025_04_15_100002_phase1_add_soft_delete_to_customer_table.php +``` + +### Schritt 2: Duplikate analysieren (dry-run) +```bash +php artisan contacts:find-duplicates +# Optional: CSV exportieren +php artisan contacts:find-duplicates --export=storage/app/duplicates.csv +# Nur bestimmte Konfidenz-Stufe +php artisan contacts:find-duplicates --confidence=HIGH +``` + +### Schritt 3: Duplikate zusammenführen +```bash +# Erst Vorschau ohne Änderungen +php artisan contacts:merge-duplicates --dry-run + +# Dann ausführen (HIGH-Konfidenz zuerst, sicherste Duplikate) +php artisan contacts:merge-duplicates --confidence=HIGH --force +php artisan contacts:merge-duplicates --confidence=MEDIUM --force + +# LOW nur nach manueller Prüfung der CSV +php artisan contacts:merge-duplicates --confidence=LOW --force +``` + +### Ergebnis-Prüfung +```sql +-- Wie viele Duplikate wurden zusammengeführt? +SELECT COUNT(*) FROM customer WHERE merged_into_id IS NOT NULL; + +-- Gibt es noch aktive Duplikate (gleiche E-Mail)? +SELECT email, COUNT(*) FROM customer +WHERE merged_into_id IS NULL AND email IS NOT NULL AND email != '' +GROUP BY email HAVING COUNT(*) > 1; +``` + +### Rollback Phase 1 +```bash +php artisan migrate:rollback --path=database/migrations/2025_04_15_100001_phase1_add_merge_fields_to_customer_table.php +``` + +--- + +## Phase 2 — Tabellen umbenennen + +> **Wichtig:** Vor Phase 2 muss der App-Code bereits auf die neuen Tabellennamen vorbereitet sein, +> ODER die Migration wird deployed bevor der Code-Release erfolgt (mit sofortigem Rollback-Plan). +> +> **Der App-Code ist vorbereitet** (siehe Abschnitt "Erledigte Code-Änderungen"). Empfehlung +> für den Live-Deploy: **Maintenance-Mode-Window** — Code-Release und Migrationen atomar +> in einem Wartungsfenster einspielen, damit Code und DB immer synchron sind. + +### Erledigte Code-Änderungen (App-Code ist Phase-2-ready) + +**Models (`app/Models/`):** +- `Customer.php`: `protected $table = 'contacts';` (mit Doku-Kommentar zu Modul 3 Phase 2) +- `Lead.php`: `protected $table = 'inquiries';` (mit Doku-Kommentar — Model-Name bleibt aus Kompatibilitätsgründen) +- `Contact.php`: `protected $table = 'contacts';` (ohne Fallback-Kommentar zu "pre-Phase-2") +- `Booking.php`: + - `@property int $inquiry_id` (ersetzt `$lead_id`) + - `$casts['inquiry_id']` + `$fillable` enthalten `'inquiry_id'` statt `'lead_id'` + - `lead()`-Relation: `return $this->belongsTo(Lead::class, 'inquiry_id');` — Methodenname bleibt für Legacy-Kompatibilität + - Zusätzlich `inquiry()`-Alias-Relation für semantische Klarheit im neuen Code + +**Repositories (`app/Repositories/`):** +- `BookingPDFRepository.php`: alle 9 Vorkommen von `$this->model->lead_id` → `$this->model->inquiry_id`. In `booking_documents.lead_id` (Shadow-Feld, Spaltenname bleibt) wird jetzt `$this->model->inquiry_id` geschrieben. +- `LeadRepository::createBooking()`: Booking-Create nutzt `'inquiry_id' => $this->model->id`. +- `CustomerMailRepository`: `customer_mails.lead_id`-Spalte bleibt erhalten, Wert kommt aus `$booking->inquiry_id`. Alle 6 Vorkommen angepasst. + +**Controllers + Services + Commands:** +- `RequestController.php`: alle Booking-Queries `where('lead_id', ...)` → `where('inquiry_id', ...)`; Datatables-SQL-Sort auf `inquiry_id`. +- `API/BookingController.php`: API-Feldname `lead_id` bleibt (Abwärtskompatibilität), Wert aus `$booking->inquiry_id`. +- `Admin/ReportController.php` + `Admin/ReportProviderController.php`: alle 16 Vorkommen von `$v->booking->lead_id` / `$export->booking->lead_id` → `...->inquiry_id`. +- `Admin/ReportLeadsController.php` + `LeadController.php`: qualifizierte Subquery `whereColumn('lead_id', 'lead.id')` → `whereColumn('lead_id', 'inquiries.id')`. +- `ContactController.php`: `select('customer.*')` → `select('contacts.*')`, `where('customer.id', ...)` → `where('contacts.id', ...)`, `DB::table('customer')` / `DB::table('lead')` → `DB::table('contacts')` / `DB::table('inquiries')`. +- `CustomerController.php`: `select('customer.*')` → `select('contacts.*')`. +- `Services/BookingImport.php`: Booking-Create-Array nutzt `'inquiry_id'`. +- `Console/Commands/ContactsMergeDuplicates.php` + `ContactsFindDuplicates.php`: alle `DB::table('customer')` / `DB::table('lead')` umgestellt. +- `Console/Commands/SyncNewsletterKulturreisen.php`: `metadata['lead_id']`-Key bleibt (ist Payload-Konvention), Wert aus `$booking->inquiry_id`. + +**Views (`resources/views/`):** +- `pdf/components/booking_header.blade.php`, `pdf/components/booking_head.blade.php`, `customer/mail/modal-show-mail-inner.blade.php`: `{{ $booking->lead_id }}` → `{{ $booking->inquiry_id }}`. +- Alle weiteren View-Treffer zu `lead_id` sind Lead-Kontext (`lead_mails.lead_id`, `lead_notices.lead_id`, HTML-Data-Attribute) und bleiben unverändert — die Spalten werden von Phase 2 nicht umbenannt. + +**Was _nicht_ umgestellt wurde (mit Absicht):** +- FK-Spalten `lead_mails.lead_id`, `lead_notices.lead_id`, `lead_files.lead_id`, `lead_participant.lead_id`, `inquiry.lead_id`, `status_history.lead_id`, `customer_mails.lead_id`, `booking_documents.lead_id` — Spaltennamen bleiben, FKs zeigen nach `RENAME TABLE` automatisch auf `inquiries.id`. +- API-Feld `lead_id` in `API/BookingController::import`-Response — Abwärtskompatibilität für API-Konsumenten. +- Metadaten-Keys `lead_id` in Newsletter-Payload. +- HTML-Data-Attribute `data-lead_id` + DataTables-Spaltennamen `lead_id` (UI-seitige Konventionen, kein DB-Bezug). +- Routen-Pfade `/lead/*` — reine UX-Arbeit, nicht blockierend für DB-Rename. Nachträglich als 301-Redirect-Paket umsetzbar. +- Routen-Namen (`lead_detail`, `lead_index`) — bleiben als Aliase, um Links in Views/Mails/Logs nicht zu brechen. + +### Schritt 1: Migrationen einspielen +```bash +# Backup erstellen! +php artisan migrate --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php +php artisan migrate --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php +php artisan migrate --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php +``` + +### Ergebnis-Prüfung +```sql +-- Tabellen vorhanden? +SHOW TABLES LIKE 'contacts'; +SHOW TABLES LIKE 'inquiries'; +-- Spalte umbenannt? +SHOW COLUMNS FROM booking LIKE 'inquiry_id'; +-- FK vorhanden? +SELECT * FROM information_schema.KEY_COLUMN_USAGE +WHERE TABLE_NAME = 'booking' AND COLUMN_NAME = 'inquiry_id'; +``` + +### Rollback Phase 2 +```bash +# In umgekehrter Reihenfolge +php artisan migrate:rollback --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php +php artisan migrate:rollback --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php +php artisan migrate:rollback --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php +``` + +--- + +## Phase 3 — Participants konsolidieren + +### Schritt 1: Neue Tabelle erstellen + Daten migrieren +```bash +# Backup erstellen! +php artisan migrate --path=database/migrations/2025_04_15_300001_phase3_create_participants_unified_table.php +``` + +### Ergebnis-Prüfung (vor Schritt 2!) +```sql +-- Zeilenzahlen vergleichen +SELECT 'lead_participant' AS src, COUNT(*) AS cnt FROM lead_participant +UNION ALL +SELECT 'participant', COUNT(*) FROM participant +UNION ALL +SELECT 'participants_unified (inquiry)', COUNT(*) FROM participants_unified WHERE inquiry_id IS NOT NULL +UNION ALL +SELECT 'participants_unified (booking)', COUNT(*) FROM participants_unified WHERE booking_id IS NOT NULL +UNION ALL +SELECT 'participants_unified (lead_contact)', COUNT(*) FROM participants_unified WHERE is_lead_contact = 1; + +-- Stichprobe: Vergleich einzelner Datensätze +SELECT * FROM lead_participant LIMIT 5; +SELECT * FROM participants_unified WHERE inquiry_id IS NOT NULL LIMIT 5; +``` + +### Schritt 2: Alte Tabellen droppen (erst nach erfolgreicher Prüfung!) +```bash +# Backup erstellen! +php artisan migrate --path=database/migrations/2025_04_15_300002_phase3_drop_old_participant_tables.php +``` + +> **Achtung:** Schritt 2 ist irreversibel. Nur ausführen wenn: +> - participants_unified seit mindestens 1 Woche stabil läuft +> - Alle PDF-Generierungen, Buchungsbestätigungen etc. korrekt funktionieren +> - Kein Rollback auf Schritt 2 geplant + +### Rollback Phase 3 +```bash +# Nur Schritt 1 rollback-fähig: +php artisan migrate:rollback --path=database/migrations/2025_04_15_300001_phase3_create_participants_unified_table.php +# Schritt 2 ist irreversibel → Backup einspielen +``` + +--- + +## Phase 4 — Communications / Notices / Attachments konsolidieren + +> **Voraussetzung:** Phase 2 muss abgeschlossen sein (inquiries + contacts Tabellen müssen existieren). + +### Schritt 1: Neue Tabellen erstellen + Daten migrieren + +Die drei Schritt-1-Migrationen sind voneinander **nicht** unabhängig: +- `400003_attachments` setzt `400001_communications` voraus (wegen `communication_id` FK) +- Reihenfolge daher: communications → notices → attachments + +```bash +# Backup erstellen! +php artisan migrate --path=database/migrations/2025_04_15_400001_phase4_create_communications_table.php +php artisan migrate --path=database/migrations/2025_04_15_400002_phase4_create_notices_table.php +php artisan migrate --path=database/migrations/2025_04_15_400003_phase4_create_attachments_table.php +``` + +### Ergebnis-Prüfung Communications +```sql +SELECT 'lead_mails' AS src, COUNT(*) FROM lead_mails +UNION ALL +SELECT 'customer_mails', COUNT(*) FROM customer_mails +UNION ALL +SELECT 'communications (lead)', COUNT(*) FROM communications WHERE legacy_source = 'lead_mail' +UNION ALL +SELECT 'communications (cust)', COUNT(*) FROM communications WHERE legacy_source = 'customer_mail'; + +-- Reply-Chain korrekt? +SELECT COUNT(*) FROM communications WHERE reply_id IS NOT NULL; +-- Muss gleich sein wie: +SELECT COUNT(*) FROM lead_mails WHERE reply_id IS NOT NULL; +-- + COUNT(*) FROM customer_mails WHERE reply_id IS NOT NULL; +``` + +### Ergebnis-Prüfung Notices +```sql +SELECT 'lead_notices' AS src, COUNT(*) FROM lead_notices +UNION ALL +SELECT 'booking_notices', COUNT(*) FROM booking_notices +UNION ALL +SELECT 'notices (inquiry)', COUNT(*) FROM notices WHERE inquiry_id IS NOT NULL +UNION ALL +SELECT 'notices (booking)', COUNT(*) FROM notices WHERE booking_id IS NOT NULL; +``` + +### Ergebnis-Prüfung Attachments +```sql +SELECT 'lead_files' AS src, COUNT(*) FROM lead_files +UNION ALL +SELECT 'booking_files', COUNT(*) FROM booking_files +UNION ALL +SELECT 'attachments (lead)', COUNT(*) FROM attachments WHERE legacy_source = 'lead_file' +UNION ALL +SELECT 'attachments (booking)', COUNT(*) FROM attachments WHERE legacy_source = 'booking_file'; + +-- communication_id korrekt verknüpft? +SELECT COUNT(*) FROM lead_files WHERE lead_mail_id IS NOT NULL; +-- Muss gleich sein wie: +SELECT COUNT(*) FROM attachments WHERE legacy_source = 'lead_file' AND communication_id IS NOT NULL; +``` + +### Schritt 2: Alte Tabellen droppen (erst nach erfolgreicher Prüfung!) + +Kann unabhängig pro Tabellenpaar eingespielt werden: + +```bash +# Backup erstellen! +# Communications droppen (achtet auf FK von lead_files → lead_mails wird intern behandelt): +php artisan migrate --path=database/migrations/2025_04_15_400004_phase4_drop_old_communication_tables.php + +# Notices droppen: +php artisan migrate --path=database/migrations/2025_04_15_400005_phase4_drop_old_notice_tables.php + +# Attachments droppen: +php artisan migrate --path=database/migrations/2025_04_15_400006_phase4_drop_old_attachment_tables.php +``` + +> **Achtung:** Reihenfolge bei Schritt 2: +> `400004_communications` muss vor `400006_attachments` laufen, +> da `lead_files.lead_mail_id` FK auf `lead_mails` zeigt und erst in 400004 entfernt wird. +> (Der FK wird in 400004 automatisch entfernt falls noch vorhanden.) + +### Rollback Phase 4 +```bash +# Schritt 1 rollback (umgekehrte Reihenfolge): +php artisan migrate:rollback --path=database/migrations/2025_04_15_400003_phase4_create_attachments_table.php +php artisan migrate:rollback --path=database/migrations/2025_04_15_400002_phase4_create_notices_table.php +php artisan migrate:rollback --path=database/migrations/2025_04_15_400001_phase4_create_communications_table.php +# Schritt 2 ist irreversibel → Backup einspielen +``` + +--- + +## Vollständige Deployment-Sequenz (alle Phasen auf einmal) + +Nur empfohlen wenn alle Phasen bereits lokal/staging getestet wurden: + +```bash +# 1. Backup +mysqldump -u [user] -p stern_crm > backup_pre_migration_$(date +%Y%m%d).sql + +# 2. Maintenance Mode aktivieren +php artisan down --render="errors::503" + +# 3. Phase 1 — Deduplizierungsfelder +php artisan migrate --path=database/migrations/2025_04_15_100001_phase1_add_merge_fields_to_customer_table.php + +# 4. Phase 1 — Duplikate zusammenführen +php artisan contacts:merge-duplicates --confidence=HIGH --force +php artisan contacts:merge-duplicates --confidence=MEDIUM --force + +# 5. Phase 2 — Umbenennung +php artisan migrate --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php +php artisan migrate --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php +php artisan migrate --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php + +# 6. Phase 3 — Participants +php artisan migrate --path=database/migrations/2025_04_15_300001_phase3_create_participants_unified_table.php + +# 7. Phase 4 — Communications / Notices / Attachments +php artisan migrate --path=database/migrations/2025_04_15_400001_phase4_create_communications_table.php +php artisan migrate --path=database/migrations/2025_04_15_400002_phase4_create_notices_table.php +php artisan migrate --path=database/migrations/2025_04_15_400003_phase4_create_attachments_table.php + +# 8. Maintenance Mode deaktivieren +php artisan up + +# 9. Testen — BEVOR die Cleanup-Migrationen laufen! +# → Daten prüfen (SQL-Queries oben), App manuell testen + +# Cleanup-Migrationen (300002, 400004-400006) SEPARAT nach Testphase einspielen! +``` + +--- + +## Abhängigkeiten zwischen Phasen + +``` +Phase 1 ──────────────────────────────────────► unabhängig +Phase 2 ──────────────────────────────────────► unabhängig (empfohlen nach Phase 1) +Phase 3 ──────────► setzt Phase 2 voraus (FK auf inquiries) +Phase 4a (comm) ──► setzt Phase 2 voraus (FK auf inquiries + contacts) +Phase 4b (not.) ──► setzt Phase 2 voraus (FK auf inquiries) +Phase 4c (att.) ──► setzt Phase 2 + Phase 4a voraus (FK auf communications) +``` + +--- + +## Rollback-Strategie: Zusammenfassung + +| Migration | Rollback möglich? | Methode | +|-----------|-------------------|---------| +| Phase 1 (merge fields) | Ja | `migrate:rollback` | +| Phase 2 (rename) | Ja | `migrate:rollback` (umgekehrte Reihenfolge) | +| Phase 3 Schritt 1 (create participants_unified) | Ja | `migrate:rollback` | +| Phase 3 Schritt 2 (drop participant tables) | **NEIN** | Backup einspielen | +| Phase 4 Schritt 1 (create comm/notices/attach) | Ja | `migrate:rollback` (umgekehrte Reihenfolge) | +| Phase 4 Schritt 2 (drop old tables) | **NEIN** | Backup einspielen | + +**Faustregel:** Alle `_create_*` Migrationen sind rollback-fähig. Alle `_drop_*` Migrationen sind irreversibel. + +--- + +## Abhängiges Modul: Newsletter (/newsletter) + +Das Newsletter-Modul (`app/Http/Controllers/NewsletterController.php`) ist **nicht direkt betroffen** von den bisherigen Änderungen, muss aber bei zukünftigen Phasen im Blick behalten werden: + +- Eigene Tabelle `newsletter_contacts` mit `customer_id` FK → `customer.id` +- Verwendet `App\Models\Customer` (altes Model) über die `customer()`-Beziehung +- Export (`/newsletter/export`) läuft über `NewsletterExport` → `NewsletterContact`-Model, nicht direkt über `customer` +- Sync-Commands: `contacts:sync-newsletter-kulturreisen`, `contacts:sync-newsletter-ferienwohnungen` + +### Was bei Phase 2 angepasst werden muss + +Wenn `customer` → `contacts` umbenannt wird (Phase 2), muss im Newsletter-Modul: +- `App\Models\Customer` → weiterhin verwenden ODER auf `App\Models\Contact` umstellen +- FK `newsletter_contacts.customer_id` zeigt weiterhin auf dieselbe Tabelle (jetzt `contacts`) — keine Migration nötig, da FK-Name sich nicht ändert, nur der Tabellenname + +### Aktueller Status +- ✅ Newsletter-Export funktioniert unverändert (Phase 1 hat nichts daran geändert) +- ✅ Phase-2-Check: `NewsletterContact` belongs-to `Customer::class` — `Customer` verwendet jetzt `$table = 'contacts'`, der FK `newsletter_contacts.customer_id` bleibt stabil. Keine Code-Änderung im Newsletter-Modul nötig. + +--- + +## Code-Änderungen nach den Migrationen + +Die Migrationen erstellen nur die neuen Tabellen und migrieren die Daten. +Der App-Code muss separat angepasst werden (nicht Teil der Migrations selbst): + +### Nach Phase 2 +- ✅ `app/Models/Customer.php`: `protected $table = 'contacts';` +- ✅ `app/Models/Lead.php`: `protected $table = 'inquiries';` +- ✅ `app/Models/Contact.php`: `protected $table = 'contacts';` +- ✅ `app/Models/Booking.php`: `lead_id` → `inquiry_id` (`$casts`, `$fillable`, `@property`, `lead()`-Relation mit expliziter FK-Spalte `inquiry_id`, neuer `inquiry()`-Alias) +- ✅ Alle Repositories, Controllers, Services, Commands und Views im Booking-Kontext auf `inquiry_id` umgestellt +- ✅ Raw-SQL (`DB::table('customer')` / `DB::table('lead')`, `select('customer.*')` / `select('lead.*')`, `whereColumn(..., 'lead.id')`) auf `contacts` / `inquiries` umgestellt +- ⬜ Route-URLs: `/lead/` → `/inquiry/` (301 Redirects) — nachgelagert, nicht blockierend für DB-Rename + +### Nach Phase 3 +- `LeadRepository::createBooking()`: Participants nicht mehr kopieren — bestehende `inquiry_id`-Einträge werden per `booking_id` ergänzt +- PDF-Generierung: auf `participants_unified` umstellen statt `lead_participant`/`participant` +- Nach Cleanup (Schritt 2): alle direkten Queries auf `lead_participant`/`participant` entfernen + +### Nach Phase 4 +- `LeadMailRepository` + `CustomerMailRepository` → `CommunicationRepository` (neu erstellen) +- `LeadFileRepository` + `BookingFileRepository` → `AttachmentRepository` (neu erstellen) +- `LeadNoticeRepository` + `BookingNoticeRepository` → `NoticeRepository` (neu erstellen) +- `MailDirService`: Datenzugriff auf `communications` umstellen +- Views für Mails/Notizen/Dateien in Lead und Booking können nach Umstellung geteilt werden diff --git a/dev/entwicklungsplan.md b/dev/entwicklungsplan.md new file mode 100644 index 0000000..2848564 --- /dev/null +++ b/dev/entwicklungsplan.md @@ -0,0 +1,732 @@ +# Entwicklungsplan mein.sterntours.de (2026) + +**Erstellt:** April 2026 +**Basis:** [briefings.md](./briefings.md), [customer-bookings/](./customer-bookings/), [audit-april-2025.md](./audit-april-2025.md), [projekt-empfehlungen-2026-04.md](./projekt-empfehlungen-2026-04.md), [frontend-navigation/](./frontend-navigation/) +**Ziel:** Konsolidierung der drei Anwendungen (`mein.sterntours.de` v3, `sterntours.de` Frontend-Backend, `v2.stern-tours.de` Reiseverwaltung) in **ein** modernes Laravel-10-CRM; Ablösung der Altsysteme; neue Kernfunktion „Angebote". + +--- + +## 0. Einordnung und Grundprinzipien + +### 0.1 Strategische Leitplanken + +- **Ein System als Zielbild:** `mein.sterntours.de` (Laravel 10) wird das einzige Backend. `sterntours.de` bleibt als Frontend-Auslieferung bestehen, das Redaktions-/Admin-Backend darin entfällt. `v2.stern-tours.de` wird komplett abgelöst. +- **Schrittweise, rückwärtskompatibel:** Jede Phase muss produktiv deploybar sein, ohne dass andere Module brechen. Parallelbetrieb alt/neu wo nötig. +- **Migrationen immer mit Rollback:** Jede DB-Migration hat einen `down()`-Pfad, Drop-Migrationen laufen erst nach bestätigter Stabilität. +- **UI-Baseline festlegen:** Bevor die Modernisierung in die Breite geht, wird ein verbindlicher UI/UX-Standard definiert (Listen, Detailseiten, Formulare, Modals, Toolbar, Filter, Inline-Edit, Tabs). Alle neuen Module werden daran ausgerichtet; Altmodule ziehen nach. +- **Business-Logik in Services, nicht in Controllern.** Neue Module nutzen konsequent den bestehenden Schichtenaufbau (Controller → FormRequest → Repository → Service → Model). + +### 0.2 Grob-Roadmap (Reihenfolge, high-level) + +``` +Block A – Fundament (Voraussetzung für alles Weitere) + A1 UI/UX-Baseline definieren + A2 Technisches Aufräumen aus Audit (API-Key, Frontend-Tooling-Entscheidung, Pint/Larastan, Queue) + A3 Customer/Lead/Booking-Neustrukturierung Phasen 2–4 abschließen + +Block B – Kerngeschäft (sichtbare Verbesserungen für Mitarbeiter) + B1 Backend-Direktbuchung (Reise + Fewo) + B2 Organisations-Tab Buchung überarbeiten + B3 E-Mail-System vereinheitlichen + Auto-Save + B4 Fewo-Doppelbuchung / Belegungs-Bug beheben + B5 Angebots-Modul (zentrale neue Funktion) + +Block C – Ablösung der Altsysteme + C1 Admin-Controller sterntours.de → mein.sterntours.de migrieren + C2 Navigation + Page-Modell refactoren + C3 CMS-Module vereinheitlichen (Navigation / Benutzerführung) + C4 Reisenverwaltung v2.stern-tours.de migrieren (größtes Modul) + +Block D – Betrieb & Langfristpflege + D1 Tests, Monitoring, Deployment + D2 Framework-/PHP-Upgrade-Pfad (Laravel 11, PHP 8.3+) + +Block E – Kundenseitige Self-Service-Funktionen (Zukunftsmodul) + E1 Kundenportal / Kunden-Login (Modul 15) +``` + +Die genannten Module sind jeweils unten ausführlich beschrieben. Block A ist zwingende Voraussetzung, danach können B und C teilweise parallelisiert werden. + +--- + +## 1. Modul 1 — UI/UX-Baseline (Block A1) + +**Ziel:** Einmalig festlegen, wie eine „moderne, einheitliche Seite" im Backend aussieht. Davon leitet sich alles andere ab. + +### 1.1 Analyse / Inventar + +- Screenshots aller Haupt-Listen (Buchungen, Anfragen, Fewo-Buchungen, Kunden, Kontakte, CMS-Listen) aufnehmen und Abweichungen dokumentieren (Filter oben / Filter als Sidebar / Suchfeld, Inline-Edit ja/nein, Pagination-Stil, Table-Buttons). +- Detailseiten (Anfrage-Detail, Buchung-Detail, Fewo-Buchung-Detail) strukturell vergleichen: Welche Sektionen / Tabs / Boxen existieren, welche sind identisch, welche unterscheiden sich unnötig? +- Typografie, Abstände, Form-Controls, Modal-Varianten auflisten. + +### 1.2 Baseline-Definition + +- Einheitliches Grid, einheitliche Toolbar (links Filter / Suche, rechts Aktionen). +- Einheitliches DataTable-Pattern (Filter → Spalten → Badge-Logik → Zeilenaktionen). +- Einheitliches Tab-Layout auf Detailseiten (linke Sidebar mit Stammdaten / Zähler, rechte Tab-Area). +- Einheitliche Modals (Bestätigung, Formular, History). +- Einheitliche Status-/Fehler-/Erfolgs-Toasts. +- Komponentenbibliothek als Blade-Components unter `resources/views/components/ui/` (Button, Card, Tabs, Toolbar, FormRow, Modal, Toast). + +### 1.3 Pilot-Umsetzung + +- Bestehende **Contacts-Liste** (bereits gut, Phase 1 abgeschlossen) als Referenz auf den Baseline-Stand anheben. +- Eine weitere Ansicht (Vorschlag: Buchungsliste) auf die Baseline portieren, um das Pattern zu validieren, bevor die Breite umgestellt wird. + +### 1.4 Frontend-Tooling-Migration (parallel, blockiert den Pilot nicht) + +**Zielkorridor (Entscheidung Abschnitt 17.7):** Vite + dart-sass + schrittweise Bootstrap 5. + +- Laravel Mix 2 → **Vite** als Build-Tool. +- `node-sass` → **dart-sass**. +- **Bootstrap 4 → 5 schrittweise:** erst Infrastruktur umstellen (Vite/Sass), Bootstrap 5 als Ziel im gleichen Branch parallel einziehen; Views ziehen **modulweise** nach, nicht in einem Big-Bang-Rewrite. +- Komponentenbibliothek aus 1.2 wird direkt auf Bootstrap 5 aufgesetzt — so zieht jedes neu gebaute Modul (ab Modul 4 aufwärts) automatisch in die neue Welt um. +- Alte Views bleiben in Bootstrap 4 lauffähig, bis ihr jeweiliges Modul umgebaut wird. +- Als eigener Feature-Branch, erst mergen, wenn Pilot-View auf neuer Pipeline läuft und parallel zu Bootstrap-4-Views ko­existieren kann. + +**Aufwand:** 2–3 Wochen (inkl. Komponenten-Library, Pilot-View). +**Abhängigkeiten:** keine. Startmodul. +**Deliverables:** Baseline-Dokument, Komponentenbibliothek, 2 Referenz-Views. + +--- + +## 2. Modul 2 — Technische Hausaufgaben (Block A2) + +Aus [audit-april-2025.md](./audit-april-2025.md) und [projekt-empfehlungen-2026-04.md](./projekt-empfehlungen-2026-04.md), bevor größere Module starten. Alle klein / mittel, aber Voraussetzung für einen sauberen Weiterbau. + +| # | Maßnahme | Priorität | Aufwand | +|---|----------|-----------|---------| +| 2.1 | Composer-Wildcards (`"*"`) durch feste Versionen ersetzen | Hoch | klein | +| 2.2 | `navigation/cache/clear`-Endpoint absichern (Auth/Secret/IP) | Hoch | klein | +| 2.3 | `booking/import`: GET entfernen, nur POST; Rate-Limit | Hoch | klein | +| 2.4 | `MailDirService`-Nutzung in Booking-/Lead-Services konsolidieren (war im Audit offen) | Mittel | mittel | +| 2.5 | API-Routen auf Klassen-Syntax (`[Controller::class, 'method']`) | Niedrig | klein, laufend | +| 2.6 | Laravel Pint + Larastan + CI-Pipeline | Mittel | mittel | +| 2.7 | PHPUnit: zweite SQLite-Connection für `mysql_stern` + Factories für Customer/Booking/Lead | Mittel | mittel | +| 2.8 | Queue-Worker (Supervisor) + `Mail::queue()` für nicht-kritische Mails | Mittel | mittel–groß | +| 2.9 | `Console\Kernel::schedule()` dokumentieren oder befüllen (keine unsichtbaren Crons) | Mittel | klein | + +**Abhängigkeiten:** keine. Läuft parallel zu A1. + +--- + +## 3. Modul 3 — Customer / Lead / Booking konsolidieren (Block A3, in Arbeit) + +**Aktueller Stand:** Phase 1 auf Testsystem abgeschlossen, Phasen 2–4 offen. Siehe [customer-bookings/umsetzung.md](./customer-bookings/umsetzung.md). + +### 3.1 Was noch zu tun ist + +1. **Phase 1 live deployen** (Duplikat-Merge). Voraussetzung: Backup + Stichprobenprüfung der CSV auf Test. +2. **Phase 2 — Tabellen umbenennen** (`customer` → `contacts`, `lead` → `inquiries`, `booking.lead_id` → `inquiry_id`). Code-Vorarbeit: Model-`$table`, Repositories auf `inquiry_id`, Routen `/lead/` → `/inquiry/` mit 301-Redirects. +3. **Phase 3 — Participants konsolidieren** (`participants_unified`). Code-Anpassung: `LeadRepository::createBooking()` kopiert keine Teilnehmer mehr; PDF-Generierung auf unified-Tabelle umstellen. **Vor dem Drop** der Alttabellen mindestens eine Woche Parallelbetrieb. +4. **Phase 4 — Communications / Notices / Attachments konsolidieren.** Neue Repositories (`CommunicationRepository`, `NoticeRepository`, `AttachmentRepository`), Views teilen, dann Alt-Tabellen droppen. + +### 3.2 Ergänzungen / Optimierungen, die in dieser Phase mitlaufen sollten + +Aus eigener Analyse nachgeschoben: + +- **Abhängiges Newsletter-Modul** (erwähnt in `umsetzung.md` §Abhängiges Modul): Nach Phase 2 `NewsletterContact`-Model auf `App\Models\Contact` mappen, damit wir keine zwei Model-Pfade für dieselbe Tabelle halten. +- **Passport-/API-Scopes:** Nach Umbenennung der Tabellen sind alle API-Responses, die `customer_id`/`lead_id` ausliefern, zu prüfen (Versionierung `v1` behalten, optional `v2` mit neuen Feldnamen). +- **Volltextsuche für Contacts:** Mit wachsender Kundenhistorie lohnt sich ein DB-Index auf `email`, `name`, `firstname`, `zip` (oder MySQL-Fulltext) — verbessert die bereits eingebaute Schnellsuche. +- **Audit-Log für Merges:** `merge_log`-Tabelle (wer hat wann welchen Contact in welchen gemerged), damit im Support nachvollziehbar bleibt. +- **DSGVO-Lösch-Workflow:** `deleted_at` existiert bereits. Braucht UI für „Kontakt komplett löschen" (mit allen Anfragen/Buchungen), inkl. Sperre, falls aktive Buchungen existieren. +- **Teilnehmer als Stammdaten:** `participants_unified.contact_id` langfristig konsequent setzen — dann können Teilnehmer aus bisherigen Reisen bei neuer Buchung übernommen werden. + +**Aufwand:** wie in `umsetzung.md` geschätzt, plus 1 Woche für die Ergänzungen oben. +**Abhängigkeiten:** Phase 2 ist Voraussetzung für das neue Angebots-Modul (Modul 6), weil sonst zwei Namenswelten parallel leben. + +--- + +## 4. Modul 4 — Backend-Direktbuchung (Block B1) + +**Briefing-Punkt:** „Es muss auch möglich sein, direkt im System Buchungen anzulegen. Aktuell gehen Mitarbeiter auf die Webseite und lösen dort eine Bestellung aus." + +### 4.1 Ist-Analyse + +- Alle Buchungen laufen aktuell über das Webseiten-Formular (`sterntours.de/src/AppBundle/Controller` → API-Endpoint `booking/import` auf `mein.sterntours.de`). +- Im Backend gibt es keinen vollständigen „Neue Buchung"-Flow; Anlage erfolgt bisher via Konvertierung aus einem Lead (`LeadRepository::createBooking()`). + +### 4.2 Teilschritte + +1. **Flow definieren (paper design):** + - Einstieg 1: „Direktbuchung ohne Anfrage" (z. B. aus `/bookings` → „Neue Buchung"). + - Einstieg 2: „Buchung aus Angebot" (kommt aus Modul 6). + - Einstieg 3: Bestehend — aus Anfrage (bleibt wie gehabt). +2. **Gemeinsames BookingFormRequest:** Validierung, die sowohl von der Web-API als auch vom Backend-Form genutzt wird (Single Source of Truth). +3. **BookingService::createManual(array $data, ?Inquiry $inquiry = null)** als zentrale Erzeugungsmethode (ersetzt Duplikatlogik zwischen Web-Import und Backend). +4. **UI-Flow im Backend (mehrstufig, nicht ein Riesenformular):** + - Schritt 1: Kontakt wählen oder anlegen (Contact-Autocomplete aus neuer Contacts-Tabelle). + - Schritt 2: Reise / Fewo wählen, Termin, Zimmer-/Personenbelegung. + - Schritt 3: Teilnehmer (aus Kontakt-Historie vorschlagen — hier greift `participants_unified.contact_id`). + - Schritt 4: Leistungen / Optionen / Versicherung. + - Schritt 5: Zahlmodalitäten, Anmerkungen. + - Schritt 6: Übersicht + „Anlegen" + „Anlegen & Bestätigung-Mail senden". +5. **Wiederverwendung des Web-Formulars:** Prüfen, ob Teile des Frontend-Formulars (`sterntours.de`) als Blade-Partials ins Backend gezogen werden können — sonst aber lieber neu auf der UI-Baseline, da das Alt-Frontend auf Twig/Symfony läuft. +6. **E-Mail-Versand optional (Entscheidung Abschnitt 17.4):** + - Letzter Schritt im Backend-Flow bietet zwei Buttons: + - **„Anlegen & Bestätigung senden"** → nutzt dieselbe Mail-Pipeline wie der Web-Import (Buchungsbestätigung an Kunde + interne Mail). + - **„Nur anlegen (keine Mail)"** → legt Buchung an, setzt ein internes Flag `silent_created = 1`, kein Kundenversand. + - Das Flag wird im Audit-Log der Buchung vermerkt, damit später nachvollziehbar ist, warum keine Bestätigung rausging. + - Nachträglicher manueller Mailversand bleibt jederzeit möglich (bestehende Mail-Funktionalität in Buchungs-Detailseite). +7. **Dokumente wie bei Web-Buchung:** Direktbuchung nutzt dieselbe Dokumenten-Logik wie `app/Http/Controllers/BookingController.php` heute (zentral hinterlegte Dokumente über `hasDocument(...)` + freie Uploads über `BookingFileRepository`) — kein Neubau. + +### 4.3 Fewo-Variante + +- Dasselbe Konzept für Fewo (`TravelUserBookingFewoController`) — mit Verfügbarkeitsprüfung in Echtzeit und harter Kollisions-Sperre (siehe auch Modul 7, Doppelbuchungen). + +**Aufwand:** ~4 Wochen (Reisebuchung + Fewo + Tests). +**Abhängigkeiten:** UI-Baseline (Modul 1), Phase 2 Customer/Lead (damit `contact_id`/`inquiry_id` sauber sind). +**Risiko:** Mittel — muss mit Web-API-Import identisch enden, sonst entstehen zwei Wahrheiten. + +--- + +## 5. Modul 5 — Organisations-Tab der Buchung überarbeiten (Block B2) + +**Briefing-Punkt:** „Unter dem Punkt Buchungen muss der Reiter Organisation verbessert werden. Es muss von der Benutzerführung einfacher und übersichtlicher werden, eine Organisation einer Reise anzulegen und zu verwalten." + +### 5.1 Ist-Analyse (TODO vor Start des Moduls) + +- Screenshots + Workflow-Walkthrough des aktuellen Organisations-Tabs dokumentieren. +- Welche Entitäten / Datensätze hängen daran (Transportleistungen, Partner, Zeiten, Dokumente)? Welche Felder sind Pflicht, welche redundant? +- Welche Reibungspunkte berichten die Mitarbeiter (Usability-Interview, 30 min). + +### 5.2 Teilschritte + +1. **Zieldesign** (aus UI-Baseline abgeleitet): Zeitleisten-/Kacheldarstellung je Leistungsposition (statt langer Tabelle), Inline-Edit für häufige Änderungen. +2. **Refactor im Daten-Layer:** Doppelte Felder identifizieren, konsolidieren (vermutlich analog zu Teilnehmer-Konsolidierung). +3. **UI-Umsetzung** auf Basis der neuen Komponentenbibliothek. +4. **Bulk-Aktionen:** Leistungsblock kopieren (für Gruppen-/Serienreisen), Vorlagen speichern. +5. **PDF-Output** prüfen (Organisationsplan als Dokument). + +**Aufwand:** ~2–3 Wochen. +**Abhängigkeiten:** Modul 1. Kein harter Zusammenhang zu Modul 3, aber wirkt sauberer, wenn Modul 3 Phase 3 (Participants) vorher durch ist. + +--- + +## 6. Modul 6 — Angebote / Offers (Block B5, großer neuer Kernbaustein) + +**Briefing-Zitat (Kunde):** „Wir möchten aus unserem System unbedingt einfach, schnell und qualitativ hochwertig Angebote erstellen können … Theoretisch kann ein solches Angebot aussehen wie unser Buchungsauftrag, nur dass ‚Angebot' darüber steht … Wichtig ist uns, dass es einfach zu erstellen ist und man ggf. bei einer sehr hochwertigen Reise auch weiterführenden Text zum Reiseverlauf für die Reisebeschreibung hinzufügen kann … Anschließend wäre es gut, wenn hieraus direkt eine Buchung generiert werden könnte." + +### 6.1 Ziel + +Aus einer Anfrage (oder direkt) ein **Angebot** erstellen, als PDF ausgeben, an den Kunden versenden und bei Annahme **mit einem Klick in eine Buchung umwandeln**, ohne Daten doppelt zu pflegen. + +### 6.2 Datenmodell + +Neue Tabellen (Entwurf): + +```sql +-- Logischer Angebotsdatensatz (eine "Angebots-Nummer", mehrere Versionen möglich) +CREATE TABLE offers ( + id INT PK, + inquiry_id INT NULL, -- aus Anfrage erzeugt + contact_id INT NOT NULL, + booking_id INT NULL, -- gesetzt, sobald Angebot → Buchung wurde + offer_number VARCHAR UNIQUE, -- eigene Nummernkreisführung, z. B. 2026-00123 + status ENUM('draft','sent','accepted','declined','expired','withdrawn'), + current_version_id INT NULL, -- FK auf offer_versions.id (aktiv/zuletzt versendet) + created_by INT, + created_at, updated_at +); + +-- Jede versendete (und jede danach geänderte) Fassung ist eine eigene Version +CREATE TABLE offer_versions ( + id INT PK, + offer_id INT FK, + version_no INT, -- 1, 2, 3, ... + status ENUM('draft','sent','accepted','declined','expired','superseded'), + valid_until DATE NULL, + total_price DECIMAL(10,2), + headline VARCHAR, -- "Persönliches Angebot für Frau XXX" + intro_text TEXT, + itinerary_text LONGTEXT, -- Reiseverlauf, optional WYSIWYG + closing_text TEXT, + template_id INT NULL, + pdf_path VARCHAR NULL, -- generiertes PDF dieser Version + pdf_archived TINYINT(1) DEFAULT 0, -- für spätere Speicher-Bereinigung alter Versionen + sent_at DATETIME NULL, + accepted_at DATETIME NULL, + accepted_via ENUM('customer_link','admin','email') NULL, + created_by INT, + created_at, updated_at, + UNIQUE KEY (offer_id, version_no) +); + +CREATE TABLE offer_items ( + id INT PK, + offer_version_id INT FK, -- gehört zu einer Version, nicht zum Angebot direkt + position INT, + type ENUM('travel','service','option','discount','insurance','custom'), + title VARCHAR, + description TEXT, + quantity INT DEFAULT 1, + price_per_unit DECIMAL(10,2), + total_price DECIMAL(10,2), + travel_program_id INT NULL, + fewo_lodging_id INT NULL, + metadata JSON +); + +CREATE TABLE offer_templates ( + id, name, description, headline, intro_text, itinerary_text, closing_text, items_json, ... +); + +-- Kundenseitiger Freigabe-Link (Token-URL, Einmal-Link pro Version) +CREATE TABLE offer_access_tokens ( + id INT PK, + offer_version_id INT FK, + token VARCHAR(64) UNIQUE, + expires_at DATETIME NULL, + opened_at DATETIME NULL, -- wann der Kunde den Link erstmals geöffnet hat + accepted_at DATETIME NULL, + declined_at DATETIME NULL, + ip_address VARCHAR(45) NULL, + user_agent VARCHAR(255) NULL +); +``` + +Angebote sind **eigenständig**, nicht Unterdatensatz einer Buchung. Eine Buchung kann per `offer_id` rückverweisen, und ein Angebot hält per `booking_id` die daraus entstandene Buchung. Die Versionierung ist zwingend (Entscheidung aus Abschnitt 17.1): ab dem ersten Versand ist Änderung = neue Version. + +### 6.3 Teilschritte + +1. **Datenmodell & Migrationen** (`offers`, `offer_versions`, `offer_items`, `offer_templates`, `offer_access_tokens`). +2. **Model / Repository / Service** (`OfferService::createFromInquiry($inquiryId)`, `::createNewVersion(Offer $offer)`, `::send(OfferVersion $v)`, `::convertToBooking(Offer $offer)`). +3. **Vorlagen-Verwaltung:** + - Eigene Liste `/offer-templates`, anlegbar aus einem existierenden Angebot (Button „Als Vorlage speichern"). + - Vorlagen pro Reise-Organisation gruppierbar (Briefing: „aus einer der Vorlagen eine Organisation laden"). +4. **Angebots-Editor:** + - Einstieg: innerhalb `/inquiries/{id}` Button „Angebot erstellen"; alternativ eigenständiges `/offers/create`. + - Vorlage wählen → Felder werden gefüllt → frei bearbeitbar. + - Positionen (offer_items) mit Drag&Drop, Kopier-Button, Summe live. + - Optionales Feld „Reiseverlauf" als WYSIWYG (TinyMCE/TipTap), ausblendbar für einfache Angebote. + - **Dokumente am Angebot** (analog zum bestehenden Buchungs-Pattern in `app/Http/Controllers/BookingController.php`, der `booking.hasDocument('coupon'|'storno'|…)` für zentral hinterlegte Dokumente und `BookingFileRepository` für freie Uploads nutzt): + - **Zentral hinterlegte Dokumente** (Briefbögen, AGB, Standard-Anhänge pro Reise-Organisation / Reisetyp) werden auswählbar und automatisch als Anhang angeboten. + - **Freie Uploads pro Angebot** (individueller Reiseverlauf, eigener Briefbogen, Bilder) — analog zu `booking_file` über eine `offer_file`-Tabelle bzw. über das in Modul 3 Phase 4 konsolidierte `attachments`-Modul (dann mit `offer_version_id`). + - Ab `sent` werden die zu diesem Zeitpunkt ausgewählten/hochgeladenen Dokumente mit der Version „eingefroren" (zur Version gespeichert, damit spätere Änderungen keine alten PDFs verfälschen). +5. **PDF-Generierung:** + - Neues Template `pdf/offer.blade.php` analog zum bestehenden Buchungsauftrag-PDF. + - Header: „Angebot Nr. 2026-00123 / V2" und / oder „Persönliches Angebot für Frau Musterfrau". + - Gleiche Corporate-Identity-Elemente wie Buchungsauftrag. + - Anlage: zentral ausgewählte Dokumente + frei hochgeladene Dokumente. +6. **Versionierung (fest verankert, Entscheidung Abschnitt 17.1):** + - Solange Status `draft`: Änderungen ändern die aktuelle Version direkt. + - Ab dem ersten `send`: jede Änderung erzeugt eine **neue Version** (V2, V3 …); vorherige Version wird `superseded`. + - Jede Version hält ihr eigenes PDF + ihre eigenen Dokumente + ihre eigenen `offer_items` + ihren eigenen Freigabe-Token. + - **Speicher-/Archivstrategie:** Cron-Job (später, Modul 13) setzt PDFs älterer Versionen `pdf_archived = 1` und verschiebt sie auf kalten Storage; konfigurierbares Retention-Limit (z. B. „nur letzte 3 Versionen im Hot-Storage, ältere PDFs bei Bedarf neu generieren"). +7. **Versand-Workflow:** + - Status-Maschine auf `offer_versions`: `draft → sent → accepted/declined/expired/superseded`. + - Versand als E-Mail (PDF im Anhang, Textvorlage im CMS pflegbar — siehe Modul 11). + - Mail-Thread landet im gemeinsamen Communications-Modul (Modul 3 Phase 4). + - Beim Senden wird pro Version **ein neuer `offer_access_token`** erzeugt und in die Mail als Link eingebaut. +8. **Kundenseitiger Freigabe-Link (Entscheidung Abschnitt 17.2):** + - Öffentliche Route `GET /angebot/{token}` auf `sterntours.de` (oder Subdomain `angebote.sterntours.de`), die die Angebotsversion anzeigt (PDF-Embed + Anhänge + Buttons „Annehmen" / „Ablehnen" / „Fragen?"). + - Route `POST /angebot/{token}/accept` und `POST /angebot/{token}/decline` mit IP/User-Agent-Protokoll. + - Beim Annehmen: `offer_versions.status = accepted`, `accepted_at = now()`, `accepted_via = 'customer_link'`, Benachrichtigung (Mail + Dashboard-Hinweis) an den zuständigen Mitarbeiter. + - **Admin-Annahme bleibt gleichwertig möglich** (Status händisch auf `accepted` setzen, `accepted_via = 'admin'`), z. B. bei telefonischer Zusage. +9. **Konvertierung Angebot → Buchung:** + - Button „In Buchung übernehmen" im Angebot (nur aktiv bei `accepted`). + - `OfferService::convertToBooking()` legt Buchung + Teilnehmer + Leistungen auf Basis der angenommenen `offer_version` an; nutzt denselben Service wie Modul 4 (Direktbuchung), damit nur eine Erzeugungslogik existiert. + - Angebots-Dokumente (Version-eingefroren) werden automatisch an die Buchung kopiert / verlinkt. + - `booking_id` wird auf `offers` gesetzt. +10. **Übersichtsseite / Listen:** `/offers` mit Filtern (Status, Ablaufdatum, Mitarbeiter, Kontakt, Version), Badges, Bulk-Export. +11. **Zähler / Dashboard:** In der Kontakt- und Anfrage-Übersicht jeweils Badge „X Angebote" analog zum Anfragen-/Buchungs-Badge. +12. **Berechtigungen:** Neue Permissions `offers.read`, `offers.create`, `offers.send`, `offers.accept`. + +### 6.4 Ergänzungen aus meiner Sicht (nicht im Briefing, aber sinnvoll) + +- **Ablauf-Automatik:** Cron setzt Angebote nach `valid_until` auf `expired`, optional Erinnerungsmail an Mitarbeiter vor Ablauf. Token wird dabei invalidiert. +- **Änderbarkeits-Regel:** Nur `draft`-Versionen sind frei änderbar. Ab `sent` ist Änderung = neue Version. +- **Preiskonsistenz:** `offer_items` greifen — sofern verknüpft — auf dieselben Preisquellen wie das bestehende Reise-/Fewo-System (aus v2 migriert, Modul 12). Keine Doppeltpflege von Preislisten. +- **Anbindung Kundenportal (Modul 15):** Sobald das Kundenportal steht, sieht der eingeloggte Kunde seine Angebote direkt dort — der Token-Link bleibt aber für nicht eingeloggte Kunden weiterhin der Standardweg. + +**Aufwand:** ~7–9 Wochen (großes Modul, durch Versionierung + Freigabe-Link leicht gewachsen). +**Abhängigkeiten:** +- UI-Baseline (Modul 1) muss stehen. +- Modul 3 Phase 2 (inquiries-Tabelle), damit `inquiry_id`-Referenzen sauber sind. +- Modul 3 Phase 4 (attachments-Tabelle) ideal — sonst legen wir eine separate `offer_file`-Tabelle an, die später fusioniert. +- Modul 4 (gemeinsamer BookingService), damit die Konvertierung keinen Copy-Code erzeugt. +- Modul 12 Teil „Reiseprogramme" verfügbar in Laravel, falls `offer_items` auf bestehende Programme referenzieren sollen — zur Not über View-basiertes Legacy-Lesemodell überbrückbar. + +--- + +## 7. Modul 7 — E-Mail-System vereinheitlichen & Auto-Save (Block B3) + +**Briefing-Punkte:** +- „Unter dem Punkt Buchungen gibt es E-Mails … wird eine E-Mail geschrieben. Soll diese sofort per Auto-Save als Entwurf gespeichert werden." +- „Auch hier [Fewo] müssen die E-Mails, die dem selben Prinzip die Buchung verfolgen, auch ein Auto-Save bekommen." +- „Da die E-Mails im System grundsätzlich eine einheitliche Programmierung haben, wäre es sinnvoll, diese Skripte zu migrieren." + +### 7.1 Teilschritte + +1. **Konsolidierung zuerst erledigen** (Modul 3 Phase 4, `communications`-Tabelle). Solange drei verschiedene Mail-Tabellen existieren, ist jede Erweiterung doppelte Arbeit. +2. **Draft-Feld einführen** in `communications`: Status `draft | queued | sent | failed`, plus `autosaved_at`. +3. **Auto-Save im Editor (Frontend):** + - JS-Debounce (alle 3 s) schreibt per `PATCH /communications/{id}/draft` Betreff und Body. + - Anlage eines Drafts beim Öffnen des leeren Editors (liefert `id` zurück). + - Crash-/Reload-Recovery: Beim Öffnen des Mail-Dialogs prüft das Frontend, ob ein bestehender Draft für diesen Kontext existiert (Anfrage / Buchung / Fewo-Buchung) und bietet ihn zum Weiterbearbeiten an. +4. **Vereinheitlichte Modals** auf Basis der UI-Baseline (ein Mail-Modal für alle drei Kontexte). +5. **Queue-basierter Versand** (siehe Modul 2 / 2.8): `Mail::queue()`, mit `send_after`-Feld (geplanter Versand), Retry-Logik. +6. **CustomerFewoMail → Communications migrieren** (Ausnahme, die in Modul 3 Phase 4 ggf. noch offen ist). +7. **Draft-Aufräumcron** (automatische Löschung von Drafts > 30 Tage ohne Aktivität). + +**Aufwand:** ~2 Wochen nach Abschluss von Modul 3 Phase 4. +**Abhängigkeiten:** Modul 3 Phase 4. + +--- + +## 8. Modul 8 — Fewo-Doppelbuchungen / Belegung fixen (Block B4) + +**Briefing:** „Bei den Buchungen der Ferienwohnungen tauchen immer wieder Fehler auf bei der Belegung … wenn die Daten geändert werden … Doppelbuchungen teilweise im System vorliegen. Ich glaube nur in der Datenbank, das muss natürlich bereinigt werden." + +### 8.1 Analyse + +- Root-Cause vermutet (aus Briefing): Daten-Änderungen werden nicht synchron in die zweite Speicher-Ebene durchgeschrieben (vermutlich Legacy-Admin in `sterntours.de/AdminController.php` vs. `mein.sterntours.de`). +- Ziel von Modul 9 (Admin-Migration) wird diesen Fehler indirekt mit beheben, der Bug muss aber separat gezielt adressiert werden — sonst blutet er weiter. + +### 8.2 Teilschritte + +1. **Quellen inventarisieren:** Welche Stellen schreiben `FewoReservation` / Belegungen (Legacy AdminController, Webseite, mein.sterntours Fewo-Buchungen, Fewo-Mail-Import, ggf. iCal-Sync)? +2. **Konflikt-Detektor-Cron schreiben:** Stündlicher Job, der Überlappungen pro `fewo_lodging_id` findet und einen Report ins Dashboard + Slack/Mail liefert. +3. **Datenbestand aufräumen:** Einmaliger Report → manuelle / halb-automatische Bereinigung. +4. **Zentrale Schreiblogik einziehen:** Alle Fewo-Reservierungs-Anlagen / -Änderungen laufen künftig durch einen einzigen `FewoReservationService`, der + - Überlappungen mit Lock prüft (`SELECT … FOR UPDATE`), + - Konsistent in allen relevanten Tabellen schreibt, + - Events feuert (für Cache-Invalidierung, Kalender). +5. **Turnustag-Logik (Entscheidung Abschnitt 17.5):** + - **Kein harter DB-Constraint** (kein `EXCLUSION`-/Unique-Index auf Tages-Ebene), weil An- und Abreisetag legitimerweise überlappen dürfen (Abreise-Vormittag / Anreise-Nachmittag am selben Tag). + - Überlappung wird **im Service** nach klaren Regeln geprüft: + - Konflikt = neue Buchung endet **später** als bestehende beginnt UND neue Buchung beginnt **früher** als bestehende endet, **und** die überlappenden Tage sind keine reinen Wechseltage (Turnustage). + - „Wechseltag" konfigurierbar pro Lodging (Tagestyp: frei / nur Anreise / nur Abreise / gesperrt). + - Der Service liefert bei Konflikt eine sprechende Fehlermeldung („Belegung kollidiert mit Buchung #1234 vom 12.–19.06., kein Wechseltag"), statt stumm abzulehnen. +6. **Reservierungs-Audit-Log:** Jede Änderung protokollieren (alter/neuer Zeitraum, User, Quelle). +7. **UI-Kalender für Mitarbeiter:** Visueller Belegungskalender pro Lodging mit farblich markierten Wechseltagen — macht das „überlappen darf, aber nur am Turnustag" für Mitarbeiter sofort sichtbar. + +**Aufwand:** ~2 Wochen (Analyse + Fix + Bereinigung). +**Abhängigkeiten:** kann früh gestartet werden; überschneidet sich inhaltlich mit Modul 9 (Admin-Migration) und muss dort mitgedacht werden. + +--- + +## 9. Modul 9 — Migration Admin aus sterntours.de (Block C1) + +**Briefing:** „Zusätzlich gibt es ein weiteres Backend `sterntours.de/src/AppBundle/Controller/AdminController.php`. Dieses muss auch sauber in das Hauptbackend `mein.sterntours.de` (v3) übernommen werden, damit wir das alte abstellen können. Damit wird sich dann vermutlich auch der Fehler erledigen. Grundsätzlich darf gerne die Datenstruktur migriert werden und optimiert werden." + +### 9.1 Scope-Analyse + +`AdminController.php` (~1.300 Zeilen) enthält laut Code u. a.: +- Fewo-Lodging-Verwaltung (CRUD, Bilder, Gruppen). +- Fewo-Preise, Saisons, Reservierungen. +- Fewo-Booking-Requests. + +### 9.2 Teilschritte + +1. **Inventar:** Jede Route in `AdminController.php` (+ evtl. zugehörige Templates) auflisten, Funktion beschreiben, Äquivalent im Laravel-System suchen. +2. **Datenstruktur-Analyse:** Welche Tabellen werden bedient (`fewo_lodging`, `fewo_price`, `fewo_season`, …)? Welche liegen schon in `mein.sterntours.de`, welche müssen migriert / umbenannt werden? Wo existiert doppelte Datenhaltung (Ursache für Modul 8)? +3. **Zieldesign:** Pro Funktion ein Ticket/Epic (Fewo-Lodging-Verwaltung, Preise, Saisons, …). Jede Funktion zieht auf die UI-Baseline (Modul 1). +4. **Migrationssequenz:** + - a) Read-Only-Schattenansicht in `mein.sterntours.de` bauen, die Daten aus der bestehenden Tabelle anzeigt. + - b) Schreib-Operationen parallel einbauen (Dual-Write-Modus: Legacy + neu), solange beide laufen. + - c) Schreib-Rechte im Legacy-Admin entziehen, sobald die neue UI stabil ist. + - d) Legacy-Admin vollständig deaktivieren (Routing/Login weg). +5. **Integrationstests pro Funktion** (zwingend, da sonst Modul 8 erneut entsteht). + +### 9.3 Reihenfolge-Empfehlung innerhalb C1 + +1. Fewo-Lodging-CRUD (niedriges Risiko, häufigste Aufgabe). +2. Fewo-Preise + Saisons (mittel, zentral für Buchungen). +3. Fewo-Reservierungen (hohes Risiko — muss mit Modul 8 zusammen gedacht werden). +4. Fewo-Booking-Requests + Bilder. + +**Aufwand:** ~8–10 Wochen abhängig von Funktionsumfang. +**Abhängigkeiten:** Modul 1 (UI), Modul 2.8 (Queue) optional, eng mit Modul 8. + +--- + +## 10. Modul 10 — Navigation & Page-Modell refactoren (Block C2) + +**Briefing:** +- „Im System, gerade in Fronten `sterntours.de/src/AppBundle/Listener/KernelControllerListener.php`, ist die Struktur der Navigation und der des Seitenbaums sehr eigenwillig und muss verbessert und optimiert werden. `mein.sterntours.de/app/Models/Page.php` — alles liegt in dieser Page-Tabelle und ist sehr schlecht wartbar und undurchschaubar. Hier muss ein deutlich cleaneres Konzept her." +- `https://mein.sterntours.test/navigation-api` ist als Ansatz vorhanden, aber noch nicht gut. + +### 10.1 Ist-Zustand (aus navigation.md / frontend-navigation/) + +- Ein Symfony `KernelControllerListener` übernimmt Routing-Entscheidungen basierend auf `Page.realUrlPath` / Slug-Traversierung / hartkodierten Redirects / Template-Namen. +- Alles liegt in **einer** Tabelle `page`, die sowohl Inhalte, Reiseprogramme, Fewo-Zuordnungen, Länder, News, Reiseführer referenziert. +- Nested-Set-Logik (`lft`/`rgt`/`lvl`) ist unzuverlässig; derzeit wird sie im Listener bewusst umgangen. +- Es existiert eine Backend-UI unter `/navigation-api`, aber sie ist read-only und deckt das Frontend-Mental-Modell nicht sauber ab. + +### 10.2 Zielbild (Vorschlag) + +Trennung in klar benannte Entitäten: + +``` +pages — generische Inhaltsseiten (CMS-Content) +menu_items — reine Navigationsknoten (Titel, Link, Sortierung, parent_id) +routes/redirects — URL-Routing (Slug → Ziel-Entität) inkl. 301-Redirects +content_types — Polymorphe Zuordnung: menu_item → [Page | TravelProgram | FewoLodging | Country | News | TravelGuide | ...] +``` + +- `menu_items` ist der **einzige** Baum (adjacency list, evtl. mit `nested-set`-Paket gepflegt). +- Jede `menu_item` hat einen `linkable_type` + `linkable_id` (Polymorphie) — so wird klar, was hinter einem Link steht, und das Model `Page` ist nicht mehr das Mega-Modell. +- `routes`-Tabelle auflöst Slug → `linkable`, ersetzt `realUrlPath`-Suche. + +### 10.3 Teilschritte + +1. **Konzept-Doku** mit allen Alt-Datenfeldern in `page` und Zielzuordnung (welche bleiben auf `pages`, welche wandern auf `menu_items`, welche werden zu polymorphen Zielen). +2. **Neue Tabellen aufbauen (migrativ):** `menu_items`, `routes`, optionale `redirects`. +3. **Datenmigration:** `page` → `menu_items` + `pages` + `routes` (alles ableitbar; kein Datenverlust). +4. **Laravel-Route-Driver bauen** (für Backend- und API-Lookups). +5. **Symfony-Frontend anpassen:** + - `KernelControllerListener` nutzt eine neue Lookup-API (`GET /api/routes/resolve?path=/foo/bar`) aus `mein.sterntours.de`. + - Legacy-Queries auf `page`-Tabelle entfallen dort. +6. **Neue Backend-UI** unter `/navigation` (nicht mehr `/navigation-api` read-only): + - Drag&Drop-Tree. + - Inline-Edit für Titel / Slug / Sichtbarkeit. + - „Was ist hier verlinkt?" — direkter Sprung zum Content-Editor der Zielentität. + - Live-Preview-Link ins Frontend. +7. **Redirect-Verwaltung** als eigene UI (statt hartkodierter Liste im Listener). +8. **Cache-Strategie** (Events: `RouteChanged` → Cache-Invalidierung; kein manuelles „Cache leeren"-Button mehr als primärer Weg). + +**Aufwand:** ~6–8 Wochen. +**Abhängigkeiten:** Modul 1 (UI-Pattern). Muss vor Modul 11 (CMS-Vereinheitlichung) stehen, damit CMS-Inhalte auf das neue Model angedockt werden. + +--- + +## 11. Modul 11 — CMS-Vereinheitlichung (Block C3) + +**Briefing:** Es existieren CMS-Ansätze unter `/cms/feedback`, `/cms/fewo/content`, `/cms/travel_guide/content`, `/iq/content/tree/index`, `/cms/news`, `/cms/answer_question`, `/cms/sidebar`, `/cms/content/infos`, `/cms/content/all`. „Die Einzelmodule können so bleiben. Es muss nur klarer in der Benutzerführung werden und auch aus der Hauptnavigation erreichbar sein und deutlich sein, wo was hingehört." + +### 11.1 Teilschritte + +1. **Inventar:** Jede dieser Routen inhaltlich beschreiben (Was wird gepflegt? Für welchen Frontend-Bereich?). +2. **Dachstruktur:** Eine neue Haupt-Menü-Gruppe „Inhalte / CMS" im Seitenmenü mit Unterpunkten, die klar benannt sind (nicht mehr `/cms/content/infos` vs. `/cms/content/all`). +3. **Landing-Page** `/content` als Dashboard mit Kacheln pro Bereich + Kurzbeschreibung („Was pflegst du hier?"). +4. **Konsistente Listen-/Edit-Views** (Baseline). +5. **Verknüpfung mit Navigation (Modul 10):** Aus der Navigations-UI direkt in den passenden CMS-Bereich springen („Inhalt dieser Seite bearbeiten"). +6. **Berechtigungen vereinheitlichen:** Jedes CMS-Submodul hat eine eigene Permission (`cms.feedback`, `cms.news`, …); aktuell ist das teilweise inkonsistent. +7. **Nicht ändern:** Die Einzellogik der Module (Datenmodelle bleiben). Nur UI / Dachnavigation / Permissions / Benennung. + +**Aufwand:** ~3 Wochen. +**Abhängigkeiten:** Modul 1, Modul 10 (für Verknüpfungen), Modul 3 (nur inhaltlich, keine Abhängigkeit in der Umsetzung). + +--- + +## 12. Modul 12 — Migration Reiseverwaltung v2.stern-tours.de (Block C4, größtes Einzelmodul) + +**Briefing:** „Jetzt kommt noch ein sehr großer Punkt: `v2.stern-tours.de/application/controllers/acp`. Hier werden Reisen angelegt und auch die Reisezeiträume verwaltet. Dieses ist mittlerweile absolut veraltet und auch fehleranfällig. Ein großes neues Modul wird es sein, dieses auf `mein.sterntours.de` zu migrieren — d. h. der komplette Funktionsumfang muss in das Backend einentwickelt und benutzerfreundlich gemacht werden, sowie müssen die gesamten Skripte etc. deutlich optimiert werden." + +### 12.1 Umfang (grob aus Controller-Ordner) + +``` +aegypten_api flight_period travel_country travel_insurance +catalog keyword travel_departure_point travel_option +feedback newsletter travel_destination travel_organizer +travel_arrival_point page travel_discount travel_period +travel_category travel_program travel_period_date travel_period_price +travel_class travel_program_image travel_setting travel_general_notes +wiki welcome +``` + +Das sind ~25 Entitäten / Verwaltungsbereiche. Viele hängen miteinander zusammen (Program ↔ Period ↔ PeriodDate ↔ PeriodPrice). + +### 12.2 Grundstrategie (Entscheidung Abschnitt 17.6) + +- **Stück für Stück**, nicht Big Bang. +- Pro Teil-Entität wird die **Datenstruktur überarbeitet und bereinigt** — d. h. sinnvolle Umbauten werden _mitgemacht_, nicht auf später verschoben (1:1-Portierung wäre zu teuer in Nachpflege). +- Während der Migration läuft das Altsystem auf dem Live-Server **weiter** (Parallelbetrieb, Entscheidung Abschnitt 17.8). Jedes fertig migrierte Teil-Modul übernimmt _sofort_ die Hoheit (Schreibrechte), das Altsystem geht für diesen Bereich auf Read-only bzw. Redirect ins neue Backend. +- **Teil-Migration auf Live** ist ausdrücklich vorgesehen: einzelne Entitäten dürfen produktiv auf das neue System umziehen, während andere noch in v2 bleiben. Voraussetzung: bidirektionale Datensicht (View oder Sync) für alle übergreifenden Reports. + +### 12.3 Teilschritte (hohes Level — jedes Teil-Modul wird wie ein eigenes Mini-Projekt geführt) + +1. **Inventar & Datenmodell-Review:** Pro Entität — Tabellen, Felder, Beziehungen, heute existierende Fehler / Sonderfälle. +2. **Entscheidung pro Tabelle:** Welche Felder bleiben, welche werden umbenannt, welche Tabellen werden zusammengeführt, welche Indizes fehlen. Dokumentiert als kurzes Delta-Dokument pro Entität, bevor die Migration geschrieben wird. +3. **Abhängigkeitsgraph** der Entitäten bauen → Migrationsreihenfolge ableiten. +4. **Schrittweise Migration — empfohlene Reihenfolge:** + - **Stammdaten zuerst** (wenige Abhängigkeiten, einfache CRUD): `travel_country`, `travel_class`, `travel_category`, `travel_organizer`, `travel_departure_point` (+ holiday), `travel_arrival_point`, `travel_destination`, `travel_general_notes`, `travel_insurance` + `travel_insurance_price`, `flight_period`, `travel_option`, `travel_discount`, `travel_setting`, `keyword`. + - **Reiseprogramme:** `travel_program`, `travel_program_image`, `catalog`, `page`-Zuordnungen (hängt mit Modul 10 zusammen). + - **Reisezeiträume:** `travel_period`, `travel_period_date`, `travel_period_price`, `travel_period_price_type` (eng verwoben, gemeinsam migrieren). + - **Sonderfälle / Rand:** `aegypten_api`, `newsletter`, `feedback`, `wiki`, `welcome` (Dashboard). +5. **Pro Teil-Modul:** + - Datenstruktur als Laravel-Migration (mit Rückwärtskompatibilität). + - Model + Repository + Service. + - UI auf Baseline (Listen + Edit). + - Integrationstests. + - Dual-Write während Übergang, bis das Altsystem lesefrei / abgeschaltet ist. +6. **Performance-/Query-Audit:** Laut Briefing „Skripte deutlich optimiert werden". Hier typische Ursachen angehen — N+1-Queries, fehlende Indizes, fehlende Eager Loads, zu viele JOINs in CI-Reports. +7. **Preis-Engine:** Der kritischste Teil. Alle Preisberechnungen (Programm + Periode + Rabatte + Versicherung + Optionen) müssen in **einem** Service gekapselt werden. Dieser wird dann von: + - Modul 6 (Angebote) — für `offer_items`, + - Modul 4 (Direktbuchung) — für Buchungspreise, + - Der Web-Buchungs-API, + - Dem aktuell laufenden Altsystem (während Parallelbetrieb) + genutzt. **Vermeidet eine Neuauflage des heutigen Preischaos.** +8. **Frontend-Adapter:** `sterntours.de` muss zur Anzeige (Preise, Abfahrten) dieselben Daten bekommen. Entweder API-Call ins neue Laravel oder View-Layer in DB, bis das Frontend gegen die neue Quelle läuft. +9. **Abschaltung v2:** Erst wenn alle Datenflüsse migriert sind, Legacy-Read-Zugriffe eliminiert und das Frontend gegen die neue Quelle läuft. + +**Aufwand:** ~14–20 Wochen. Durch die Entscheidung „Stück für Stück mit Überarbeitung/Bereinigung" (Abschnitt 17.6) eher am oberen Ende, dafür am Ende kein technischer Schuldenberg. +**Abhängigkeiten:** Modul 1 (UI), Modul 2 (Tooling/Tests), Modul 10 (Navigation, weil `travel_program` → `menu_items` verlinkt werden). + +--- + +## 13. Modul 13 — Betrieb, Tests, Monitoring (Block D1) + +Begleitendes Querschnittsmodul. + +### 13.1 Teilschritte + +1. **Test-Strategie:** + - Pro neues Modul: Unit-Tests für Services, Feature-Tests für kritische Flows (Direktbuchung, Angebot → Buchung, Fewo-Belegung). + - Contract-Tests für alle öffentlichen APIs (Booking-Import, Navigation-API). +2. **CI-Pipeline** (Gitlab/GitHub Actions): Pint, Larastan, PHPUnit, optional Dusk für die wichtigsten Flows. +3. **Queue-Monitoring:** Horizon (wenn Redis) oder Log-Channel + Alert bei Failed Jobs. +4. **Error-Tracking:** Sentry / Flare / Bugsnag (eines davon auswählen). +5. **Deployment-Pipeline:** Entweder Deployer/Envoyer-basiert oder Docker-Build; Maintenance-Mode-Script, Migrations- und Cache-Steps dokumentiert. +6. **Rollback-Pläne** pro Migration (ist in Modul 3 bereits vorbildlich gemacht — Standard für alle neuen Module). + +**Aufwand:** laufend, ca. 4 Wochen verteilt über das Gesamtprojekt. + +--- + +## 14. Modul 14 — Framework- & Sprach-Upgrade (Block D2) + +Nicht sofort, aber einplanen: + +- **Laravel 10 → 11 Upgrade-Pfad:** separater Branch, nach Abschluss von Block A + Teilen B. Nicht während aktiver Alt-Migration (Modul 12). +- **PHP 8.3** in Docker/CI festschreiben, aus `composer.json` die Obergrenze anheben. +- **`jenssegers/date`** entfernen, überall Carbon. +- Composer-Pakete konsolidieren (siehe `projekt-empfehlungen-2026-04.md` §2.3). + +**Aufwand:** ~2 Wochen. + +--- + +## 15. Modul 15 — Kundenportal / Kunden-Login (Zukunftsmodul, Block E) + +**Briefing (Ergänzung aus den Antworten):** „Ein weiteres Modul für die Zukunft bitte mit aufnehmen: einen Kunden-Login, so dass die Kunden Zugang haben zu ihren eigenen Buchungen und Übersichten." + +### 15.1 Ziel + +Ein öffentlich erreichbarer, eingeschränkter Kunden-Bereich, in dem sich ein Kontakt mit seiner E-Mail-Adresse anmelden und seine eigenen Daten / Anfragen / Angebote / Buchungen einsehen kann. Klar abgegrenzt vom Mitarbeiter-Backend. + +### 15.2 Datenmodell + +- **Kein separates `users`-Model** für Kunden — Auth wird an `contacts` aufgehängt, Erweiterung: + +```sql +ALTER TABLE contacts + ADD COLUMN password VARCHAR NULL, + ADD COLUMN remember_token VARCHAR(100) NULL, + ADD COLUMN email_verified_at DATETIME NULL, + ADD COLUMN portal_enabled TINYINT(1) DEFAULT 0, + ADD COLUMN last_login_at DATETIME NULL; + +CREATE TABLE customer_sessions ( + id, contact_id, ip, user_agent, last_active_at, created_at, expires_at +); +``` + +- Laravel-Auth-Guard `customer` (eigener Guard neben dem Mitarbeiter-Guard `web`). + +### 15.3 Teilschritte + +1. **Registrierung / Erstanmeldung:** + - Kunde erhält Einladung per Mail (Reiseunterlagen / Buchungsbestätigung enthalten einen „Zugang einrichten"-Link). + - Alternativ: „Passwort setzen" via E-Mail-Verifizierung ausgehend von einer bestehenden Mail-Adresse im `contacts`-Datensatz. + - Kein Self-Signup ohne bestehenden Kontakt — Datenbereinigung würde sonst erneut leiden. +2. **Login / Passwort-Reset / 2FA optional.** +3. **Portal-Views (auf `sterntours.de` oder Subdomain `mein.sterntours.de/portal` — Architekturentscheidung noch offen):** + - Dashboard („Ihre nächste Reise in X Tagen"). + - Meine Anfragen. + - **Meine Angebote** (Integration mit Modul 6: eingeloggter Kunde sieht dort dieselben Dokumente wie über den Token-Link, zusätzlich historische Angebote). + - Meine Buchungen (Dokumente, Zahlungsstand, Reiseunterlagen). + - Meine Daten (Adresse, Einwilligungen, Newsletter-Status). +4. **Datenschutz / DSGVO:** + - Nur eigene Daten sichtbar (strenge Scope-Checks in Controllern + Policies). + - Download der eigenen Daten als Export (Art. 15 DSGVO). + - Lösch-Anfrage-Flow (verknüpft mit dem `deleted_at`-Flow aus Modul 3). +5. **Integration mit Token-Links (Modul 6):** + - Ein Token-Link funktioniert weiterhin ohne Login. + - Ist der Kunde eingeloggt und klickt den Link, wird das Angebot in das Portal-Layout eingebettet (nicht doppelte Darstellung). +6. **Permissions & Sicherheit:** + - Separate Passport-Scopes für Portal-API. + - Rate-Limiting auf Login-Endpunkte. + - Audit-Log (wer hat wann was eingesehen) — wichtig bei Angebotsannahmen im Portal. +7. **UI:** Kein Teil der Mitarbeiter-UI-Baseline (andere Zielgruppe). Eigenes, leichtes Layout (Bootstrap 5), das zum `sterntours.de`-Look passt. + +### 15.4 Abhängigkeiten + +- Modul 3 Phase 2 (`contacts`-Tabelle) ist **zwingende** Voraussetzung — sonst liegen die Kundenlogins auf der alten `customer`-Tabelle. +- Modul 6 (Angebote) sollte mindestens Grundstand haben, damit Portal-Angebotsansicht sinnvoll Inhalt bekommt. +- Profitiert stark von Modul 2.8 (Queue) für Einladungs-/Benachrichtigungsmails. + +### 15.5 Einordnung in die Roadmap + +- **Nicht Teil der ersten Welle.** Frühester sinnvoller Start: nach Abschluss von Modul 6 + Modul 3 Phase 4. +- Entspricht einem eigenen Block „E — Kundenseitige Self-Service-Funktionen" und kann zeitlich mit Modul 14 (Framework-Upgrade) parallel laufen, wenn Kapazität da ist. + +**Aufwand:** ~6–8 Wochen für MVP (Login + Angebote + Buchungen + Daten), plus 2–3 Wochen für DSGVO-/Self-Service-Funktionen. + +--- + +## 16. Zusammenfassung — empfohlene Reihenfolge und Parallelisierung + +``` +Monat 1–2 A1 UI-Baseline + A2 Technik-Hausaufgaben (+ Start A3 Phase 2) + + Frontend-Migration Vite/dart-sass/Bootstrap 5 (Modul 1.4) parallel +Monat 2–3 A3 Customer/Lead/Booking Phasen 2+3 abschließen +Monat 3 A3 Phase 4 + B4 Fewo-Doppelbuchung (Turnustag-Logik) parallel +Monat 4 B1 Direktbuchung (mit Mail-Toggle) + B2 Organisation-Tab +Monat 4–5 B3 E-Mail-Auto-Save (nach A3 P4) +Monat 5–7 B5 Angebots-Modul (inkl. Versionierung + Freigabe-Link) +Monat 6–9 C1 Admin-Migration sterntours.de (zügig, kein harter Cut) +Monat 7–9 C2 Navigation + C3 CMS-Vereinheitlichung +Monat 8–16 C4 v2.stern-tours.de Reisenverwaltung — Stück für Stück mit + Teil-Migration auf Live und Parallelbetrieb +Monat ≥10 E1 Kundenportal / Kunden-Login (frühestens nach B5 + A3 P4) +Laufend D1 Betrieb/Tests, D2 Framework-Upgrade gegen Ende +``` + +Einige Module können parallelisiert werden, wenn Kapazität vorhanden ist. Kritischer Pfad: **A1 → A3 → B5 → C4**. Modul 15 (Portal) liegt off-path und kann flexibel eingeordnet werden. + +--- + +## 17. Entscheidungen (geklärt) + +Die acht zuvor offenen Fragen wurden vom Auftraggeber beantwortet. Die Entscheidungen sind in den jeweiligen Modulen bereits eingearbeitet; hier die konsolidierte Referenz: + +| # | Thema | Entscheidung | Fundstelle im Plan | +|---|-------|-------------|--------------------| +| 17.1 | **Angebots-Versionen** | Jede Änderung nach dem Versand erzeugt eine neue Version (V2, V3 …). Speicheroptimierung durch spätere Archivierung/Löschung alter Versionen. | Modul 6.2 (`offer_versions`), 6.3.6 (Versionierung), 6.4 (Archivstrategie) | +| 17.2 | **Angebots-Annahme** | Beides: Admin-seitige Statusänderung **und** kundenseitiger Freigabe-Link (Token-URL) mit Bestätigungsseite. | Modul 6.3.8 (`offer_access_tokens`, `/angebot/{token}`) | +| 17.2b | **Kundenportal** | Neues Zukunftsmodul (Modul 15): Kunden-Login mit Zugang zu eigenen Buchungen/Angeboten/Daten. | Modul 15 | +| 17.3 | **Angebots-Dokumente** | Dual-Modell analog zur heutigen Buchungs-Logik in `app/Http/Controllers/BookingController.php`: zentral hinterlegte Dokumente + freie Uploads pro Angebot. | Modul 6.3.4 | +| 17.4 | **Direktbuchung im Backend** | Gesonderter Einstieg mit Auswahl im letzten Schritt: „Anlegen & Bestätigung senden" **oder** „Nur anlegen (keine Mail)". Nachversand bleibt jederzeit manuell möglich. | Modul 4.2.6 | +| 17.5 | **Fewo-Überlappungen** | Kein harter DB-Constraint. Überlappungen werden im `FewoReservationService` nach klaren Turnustag-Regeln geprüft (An-/Abreise am selben Tag legitim). | Modul 8.2.5 | +| 17.6 | **v2-Migrationsstrategie** | Stück für Stück mit Überarbeitung und Bereinigung (keine 1:1-Portierung). | Modul 12.2 | +| 17.7 | **Frontend-Tooling** | Vite + dart-sass + schrittweise Bootstrap 5. Freigegeben. | Modul 1.4 | +| 17.8 | **Abschaltplan Altsysteme** | Parallelbetrieb, bis Migration abgeschlossen. `sterntours.de/AdminController` zügig migrieren (Modul 9). `v2.stern-tours.de` mit Teil-Migrationen auf dem Live-Server, damit der Betrieb ununterbrochen weiterläuft. | Modul 12.2 | + +### Direkt daraus folgende neue Bausteine + +- **Neue Tabellen:** `offer_versions`, `offer_access_tokens` (Modul 6). +- **Neue Routen (öffentlich):** `/angebot/{token}`, `/angebot/{token}/accept`, `/angebot/{token}/decline` auf `sterntours.de` bzw. Subdomain. +- **Neues Modul 15:** Kundenportal / Kunden-Login (eigener Block E, Zukunftsmodul). +- **Neue Service-Klasse:** `FewoReservationService` mit Turnustag-Logik (Modul 8). +- **Neue UI-Komponente:** Belegungskalender mit Wechseltag-Markierung (Modul 8.2.7). +- **Direktbuchungs-Flag:** `silent_created` auf `bookings` (Modul 4). + +--- + +## 18. Nächste konkrete Schritte (Start der Entwicklung) + +Nach dieser Abstimmung kann die Entwicklung starten. Sinnvolle erste Arbeitspakete, jeweils als eigene PRs / Tickets: + +1. **A2 Quick Wins** (sofort, blockiert nichts): + - Composer-Wildcards pinnen. + - `navigation/cache/clear` absichern. + - `booking/import` auf POST-only. + - Pint + Larastan + CI aufsetzen. +2. **A3 Phase 1 live deployen** (Customer-Deduplizierung — steht auf Test, braucht Backup + CSV-Review). +3. **A1 UI-Baseline dokumentieren** (Inventar + Baseline-Spec, ohne bereits umzubauen). +4. **Modul 1.4 Frontend-Migration** als eigener Feature-Branch starten (Vite/dart-sass/Bootstrap 5). +5. **A3 Phase 2 vorbereiten** (Code-Anpassungen `Customer::$table`, `Lead::$table`, Repositories auf `inquiry_id`). + +Schritt 1–3 können parallel angegangen werden; Schritt 4 läuft im Hintergrund, bis der Pilot-View auf der neuen Pipeline läuft. Sobald A3 Phase 2 produktiv ist, ist der Weg frei für Modul 4 (Direktbuchung) und anschließend Modul 6 (Angebote). diff --git a/dev/offers/umsetzung.md b/dev/offers/umsetzung.md new file mode 100644 index 0000000..08dec78 --- /dev/null +++ b/dev/offers/umsetzung.md @@ -0,0 +1,942 @@ +# Modul 6 — Angebote: Implementierungs-Tickets + +**Status:** Entwurf, bereit zur Umsetzung +**Erstellt:** April 2026 +**Konzept:** [../entwicklungsplan.md §6](../entwicklungsplan.md) +**Abhängigkeiten-Check vor Start:** +- Modul 3 (Customer/Lead/Booking) Phase 2 produktiv → `contacts` + `inquiries` existieren. +- Modul 3 Phase 4 (`attachments`, `communications`) ideal — sonst Fallback mit eigener `offer_files`-Tabelle, später Migration in `attachments`. +- Modul 4 (Backend-Direktbuchung) Service `BookingService::createManual(...)` verfügbar (für Konvertierung Angebot → Buchung ohne Copy-Code). Kann parallel entwickelt werden, muss vor Ticket B8 stehen. +- Modul 1 (UI-Baseline) definiert: Blade-Components `ui.toolbar`, `ui.card`, `ui.tabs`, `ui.modal`, `ui.form-row` verfügbar. + +--- + +## 0. Übersicht + +Insgesamt **6 Phasen** mit **32 Tickets**. Geschätzter Gesamtaufwand: **7–9 Wochen** (1 Entwickler). Aufsplittung erlaubt Parallelisierung von Phasen C + D + E nach Abschluss von B5. + +| Phase | Inhalt | Tickets | Aufwand | +|-------|--------|---------|---------| +| A | Fundament (DB / Models / Repositories / Services / Routing / Permissions) | A1–A7 | ~10 Tage | +| B | Admin-UI (Mitarbeiter-Backend) | B1–B9 | ~18 Tage | +| C | Vorlagen (Offer-Templates) | C1–C3 | ~3 Tage | +| D | Kundenseitiger Freigabe-Link (öffentliches Frontend) | D1–D5 | ~5 Tage | +| E | Versionierung & Archivierung | E1–E3 | ~3 Tage | +| F | Polish, Automatik, Tests | F1–F6 | ~5 Tage | + +Nach Abschluss von Phase B5 kann D parallel zu C + E gebaut werden. + +--- + +## 1. Reihenfolge und kritischer Pfad + +``` +A1 → A2 → A3 → A4 → A5 → A6 → A7 ─────────┐ + │ + ┌─── B1 → B2 → B3 → B4 → B5 ──────┼──► B6 → B7 ─┐ + │ │ │ + │ ┌── C1 → C2 ─┤ ├─► B8 → B9 + │ │ │ │ + │ ├── D1 → D2 → D3 → D4 → D5─┤ + │ │ │ + │ └── E1 → E2 → E3 ──────────┘ + │ + └──► F1 … F6 (laufend) +``` + +--- + +## Phase A — Fundament + +### Ticket A1 — Datenbank-Migrationen anlegen + +**Typ:** Migration +**Aufwand:** 1 Tag +**Abhängigkeiten:** Modul 3 Phase 2 (`contacts`, `inquiries`) + +**Dateien (neu):** +- `database/migrations/2026_05_01_100001_create_offers_table.php` +- `database/migrations/2026_05_01_100002_create_offer_versions_table.php` +- `database/migrations/2026_05_01_100003_create_offer_items_table.php` +- `database/migrations/2026_05_01_100004_create_offer_templates_table.php` +- `database/migrations/2026_05_01_100005_create_offer_files_table.php` +- `database/migrations/2026_05_01_100006_create_offer_access_tokens_table.php` +- `database/migrations/2026_05_01_100007_add_offer_id_to_bookings_table.php` + +**Schema `offers`:** +```php +Schema::create('offers', function (Blueprint $t) { + $t->id(); + $t->string('offer_number', 32)->unique(); // z. B. "2026-00123" + $t->foreignId('contact_id')->constrained('contacts')->restrictOnDelete(); + $t->foreignId('inquiry_id')->nullable()->constrained('inquiries')->nullOnDelete(); + $t->foreignId('booking_id')->nullable()->constrained('bookings')->nullOnDelete(); + $t->enum('status', ['draft', 'sent', 'accepted', 'declined', 'expired', 'withdrawn']) + ->default('draft'); + $t->unsignedBigInteger('current_version_id')->nullable(); // FK wird nachträglich gesetzt + $t->foreignId('created_by')->constrained('users'); + $t->timestamps(); + $t->softDeletes(); + + $t->index(['status', 'contact_id']); + $t->index('inquiry_id'); +}); +``` + +**Schema `offer_versions`:** +```php +Schema::create('offer_versions', function (Blueprint $t) { + $t->id(); + $t->foreignId('offer_id')->constrained('offers')->cascadeOnDelete(); + $t->unsignedInteger('version_no'); + $t->enum('status', ['draft', 'sent', 'accepted', 'declined', 'expired', 'superseded']) + ->default('draft'); + $t->date('valid_until')->nullable(); + $t->decimal('total_price', 10, 2)->default(0); + $t->string('headline')->nullable(); + $t->text('intro_text')->nullable(); + $t->longText('itinerary_text')->nullable(); + $t->text('closing_text')->nullable(); + $t->foreignId('template_id')->nullable()->constrained('offer_templates')->nullOnDelete(); + $t->string('pdf_path')->nullable(); + $t->boolean('pdf_archived')->default(false); + $t->dateTime('sent_at')->nullable(); + $t->dateTime('accepted_at')->nullable(); + $t->enum('accepted_via', ['customer_link', 'admin', 'email'])->nullable(); + $t->foreignId('created_by')->constrained('users'); + $t->timestamps(); + + $t->unique(['offer_id', 'version_no']); +}); +``` + +Danach nachträglicher FK auf `offers.current_version_id → offer_versions.id`. + +**Schema `offer_items`:** +```php +Schema::create('offer_items', function (Blueprint $t) { + $t->id(); + $t->foreignId('offer_version_id')->constrained('offer_versions')->cascadeOnDelete(); + $t->unsignedInteger('position')->default(0); + $t->enum('type', ['travel','service','option','discount','insurance','custom']); + $t->string('title'); + $t->text('description')->nullable(); + $t->unsignedInteger('quantity')->default(1); + $t->decimal('price_per_unit', 10, 2)->default(0); + $t->decimal('total_price', 10, 2)->default(0); + $t->foreignId('travel_program_id')->nullable(); // kein FK solange v2 nicht migriert + $t->foreignId('fewo_lodging_id')->nullable(); + $t->json('metadata')->nullable(); + $t->timestamps(); + + $t->index(['offer_version_id', 'position']); +}); +``` + +**Schema `offer_templates`:** +```php +Schema::create('offer_templates', function (Blueprint $t) { + $t->id(); + $t->string('name'); + $t->string('description')->nullable(); + $t->string('organization')->nullable(); // "Reise-Organisation", aus Briefing + $t->string('headline')->nullable(); + $t->text('intro_text')->nullable(); + $t->longText('itinerary_text')->nullable(); + $t->text('closing_text')->nullable(); + $t->json('items_json')->nullable(); // serialisierte Default-Positionen + $t->json('default_document_ids')->nullable(); // IDs der zentral hinterlegten Dokumente + $t->boolean('active')->default(true); + $t->foreignId('created_by')->constrained('users'); + $t->timestamps(); + $t->softDeletes(); +}); +``` + +**Schema `offer_files`** (bewusst analog zu `booking_files`, später Migration in `attachments`): +```php +Schema::create('offer_files', function (Blueprint $t) { + $t->id(); + $t->foreignId('offer_version_id')->constrained('offer_versions')->cascadeOnDelete(); + $t->string('identifier')->nullable(); // z. B. "letterhead" für feste Typen + $t->string('disk')->default('offers'); + $t->string('dir'); + $t->string('filename'); + $t->string('original_name'); + $t->string('ext', 10); + $t->string('mine', 100); // Schreibweise übernommen aus BookingFile + $t->unsignedBigInteger('size'); + $t->boolean('frozen')->default(false); // true ab Versand, um Anhang „einzufrieren" + $t->timestamps(); +}); +``` + +**Schema `offer_access_tokens`:** +```php +Schema::create('offer_access_tokens', function (Blueprint $t) { + $t->id(); + $t->foreignId('offer_version_id')->constrained('offer_versions')->cascadeOnDelete(); + $t->string('token', 64)->unique(); + $t->dateTime('expires_at')->nullable(); + $t->dateTime('opened_at')->nullable(); + $t->dateTime('accepted_at')->nullable(); + $t->dateTime('declined_at')->nullable(); + $t->string('ip_address', 45)->nullable(); + $t->string('user_agent', 255)->nullable(); + $t->timestamps(); +}); +``` + +**`add_offer_id_to_bookings_table`:** Spalte `bookings.offer_id` als nullable FK (Rückverweis bei Konvertierung). + +**Akzeptanzkriterien:** +- [ ] Alle 7 Migrationen laufen sauber durch (`artisan migrate`) und `migrate:rollback` invertiert sie. +- [ ] FKs sind konsistent; kein Drop einer Tabelle bricht andere. +- [ ] Mit `migrate:fresh --seed` reproduzierbar. + +**Risiken:** +- `travel_program_id` / `fewo_lodging_id` haben aktuell keine Laravel-Tabelle (v2). FK vorerst nicht setzen, Werte nur als Referenz speichern. Ticket beim v2-Modul: FK nachreichen. + +--- + +### Ticket A2 — Eloquent-Models + +**Typ:** Code +**Aufwand:** 1 Tag +**Abhängigkeiten:** A1 + +**Dateien (neu):** +- `app/Models/Offer.php` +- `app/Models/OfferVersion.php` +- `app/Models/OfferItem.php` +- `app/Models/OfferTemplate.php` +- `app/Models/OfferFile.php` +- `app/Models/OfferAccessToken.php` + +**`Offer.php`:** +- `$fillable`, `$casts` (status als Enum, Timestamps). +- Relationen: `contact()`, `inquiry()`, `booking()`, `versions()`, `currentVersion()`, `createdBy()`. +- Scopes: `scopeOpen()`, `scopeForContact($q, $id)`, `scopeWithStatus($q, array $stati)`. +- Factories: `HasFactory`. + +**`OfferVersion.php`:** +- Relationen: `offer()`, `items()`, `files()`, `tokens()`, `template()`, `createdBy()`. +- Helper: `isEditable()` (true wenn status=draft), `latestToken()`, `totalPriceFormatted()`. +- Dateipfad-Helper: `getPdfUrl()` analog zu `BookingFile::getURL()`. + +**`OfferFile.php`:** +- Gleiche Methoden wie `BookingFile::getURL()`, `::getIconExt()`, `::formatBytes()` — Interface-kompatibel, damit derselbe Frontend-Code verwendbar bleibt. + +**`OfferAccessToken.php`:** +- `::generate(OfferVersion $v, ?Carbon $expiresAt = null): self` — erzeugt kryptografisch sicheren Token (`Str::random(64)`), speichert, gibt zurück. +- `scopeActive()` — nicht expired, nicht declined. + +**Akzeptanzkriterien:** +- [ ] `Offer::factory()->create()` funktioniert. +- [ ] `$offer->currentVersion->items` liefert korrekte Collection. +- [ ] `OfferAccessToken::generate()` erzeugt unique Token, Kollisionsschutz per `unique`-Constraint. + +--- + +### Ticket A3 — Repositories + +**Typ:** Code +**Aufwand:** 1 Tag +**Abhängigkeiten:** A2 + +**Dateien (neu):** +- `app/Repositories/OfferRepository.php` extends `BaseRepository` +- `app/Repositories/OfferVersionRepository.php` extends `BaseRepository` +- `app/Repositories/OfferFileRepository.php` extends `FileRepository` (analog `BookingFileRepository`) +- `app/Repositories/OfferTemplateRepository.php` extends `BaseRepository` + +**`OfferRepository`:** +- `create(array $data, int $contactId, ?int $inquiryId = null): Offer` — legt `Offer` + `OfferVersion#1` (status=draft) an, setzt `current_version_id`. +- `generateOfferNumber(): string` — Format `{YYYY}-{5-stellig laufend}`, nutzt `DB::transaction` mit `SELECT … FOR UPDATE` auf einer Zähler-Tabelle ODER nimmt `MAX(offer_number)` mit Transaktion. +- `findByNumber(string $no): ?Offer` + +**`OfferVersionRepository`:** +- `updateDraft(OfferVersion $v, array $data): OfferVersion` — nur wenn `status=draft`; Items werden aus `$data['items']` synchronisiert. +- `createNewVersion(Offer $offer): OfferVersion` — dupliziert aktuelle Version inkl. items + files (als neue Zeilen), `version_no = max+1`, vorherige wird `superseded`, `offer.current_version_id` wird aktualisiert. Atomar in Transaktion. +- `markSent(OfferVersion $v): OfferVersion` — setzt status, `sent_at`, `frozen` auf allen `offer_files`. + +**`OfferFileRepository` (analog `BookingFileRepository`):** +- `__construct(OfferFile $model)` +- `save()` schreibt mit `$this->offer_version_id`, `$this->identifier`, `$this->dir = /files/YYYY/MM/`. +- `response()` identische JSON-Struktur wie `BookingFileRepository::response()`, damit Frontend-Code geteilt werden kann. + +**Akzeptanzkriterien:** +- [ ] `OfferRepository::create()` erzeugt Offer + initiale Version atomar; Rollback bei Fehler. +- [ ] `generateOfferNumber()` ist race-safe (parallele Tests erzeugen keine Duplikate). +- [ ] `createNewVersion()` kopiert Items + Files, Preise bleiben gleich, alte Version `superseded`. + +--- + +### Ticket A4 — OfferService (Business-Logik) + +**Typ:** Code +**Aufwand:** 2 Tage +**Abhängigkeiten:** A3 + +**Dateien (neu):** +- `app/Services/OfferService.php` + +**Methoden:** +```php +public function createFromInquiry(int $inquiryId, ?int $templateId = null): Offer; +public function createBlank(int $contactId, ?int $templateId = null): Offer; +public function applyTemplate(OfferVersion $v, OfferTemplate $template): OfferVersion; +public function updateVersion(OfferVersion $v, array $data): OfferVersion; +public function send(OfferVersion $v, array $mailData): void; // erzeugt Token, versendet Mail, wechselt Status +public function markAccepted(OfferVersion $v, string $via, ?string $ip = null, ?string $ua = null): void; +public function markDeclined(OfferVersion $v, string $via, ?string $reason = null): void; +public function supersede(OfferVersion $v): OfferVersion; // erzeugt neue Version +public function withdraw(Offer $offer, string $reason): void; +public function convertToBooking(Offer $offer): Booking; // delegiert an BookingService::createManual() +``` + +**Logikregeln:** +- `send()` nur möglich, wenn Version `status=draft` und `current_version` der Offer ist. +- `markAccepted()` nur möglich bei `status=sent`; Offer.status wechselt ebenfalls auf `accepted`; ältere noch aktive Versionen werden `superseded`. +- `supersede()` deaktiviert bestehenden Token, erzeugt aber beim nächsten `send()` einen neuen. +- `convertToBooking()` nur bei Offer.status=accepted; nutzt `BookingService::createManual($offer->currentVersion->toBookingData())`; setzt `offer.booking_id`, kopiert `offer_files` als `booking_files`. + +**Akzeptanzkriterien:** +- [ ] Unit-Tests für jede öffentliche Methode (happy path + Statusguard-Fehler). +- [ ] `convertToBooking()` arbeitet idempotent — zweiter Aufruf wirft DomainException. +- [ ] Service wirft sprechende, eigene Exceptions (`OfferNotEditableException`, `InvalidOfferStatusException`). + +--- + +### Ticket A5 — FormRequests + +**Typ:** Code +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** A3 + +**Dateien (neu):** +- `app/Http/Requests/Offer/StoreOfferRequest.php` +- `app/Http/Requests/Offer/UpdateVersionRequest.php` +- `app/Http/Requests/Offer/SendOfferRequest.php` +- `app/Http/Requests/Offer/OfferTemplateRequest.php` + +Validierungen pro Feld (Preise ≥ 0, `valid_until` in Zukunft, HTML-Sanitizing für `itinerary_text`, Dateiuploads MIME-Typen wie bei `BookingFile`). + +--- + +### Ticket A6 — Routing + Permissions + +**Typ:** Code +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** A4 + +**Dateien (geändert):** +- `routes/web.php` — neuer Block analog zu `bookings`/`leads`: + +```php +Route::group(['middleware' => ['auth.2fa']], function () { + Route::get('data/table/offers', 'OfferController@getOffers')->name('data_table_offers'); + Route::get('/offers/{step?}', 'OfferController@index')->name('offers'); + Route::get('/offer/detail/{id}', 'OfferController@detail')->name('offer_detail'); + Route::post('/offer/detail/{id}', 'OfferController@store')->name('offer_detail_store'); + Route::post('/offer/modal/load', 'OfferController@loadModal')->name('offer_modal_load'); + Route::get('/offer/action/{action}/{id?}', 'OfferController@action')->name('offer_action'); + Route::post('/offer/action/{action}/{id?}', 'OfferController@action')->name('offer_action_store'); + Route::get('/offer/delete/{id}/{del?}', 'OfferController@delete')->name('offer_delete'); + Route::get('/offer/pdf/{versionId}', 'OfferController@pdf')->name('offer_pdf'); + + // Templates + Route::get('/offer-templates', 'OfferTemplateController@index')->name('offer_templates'); + Route::get('/offer-template/detail/{id}', 'OfferTemplateController@detail')->name('offer_template_detail'); + Route::post('/offer-template/detail/{id?}', 'OfferTemplateController@store')->name('offer_template_detail_store'); + Route::get('/offer-template/delete/{id}', 'OfferTemplateController@delete')->name('offer_template_delete'); + + // Files (analog zu BookingController action-upload-booking-file) + Route::post('/offer/upload/{versionId}', 'OfferFileController@upload')->name('offer_file_upload'); + Route::get('/offer/file/delete/{id}', 'OfferFileController@delete')->name('offer_file_delete'); +}); +``` + +**Permissions** (in Seeder `database/seeds/PermissionSeeder.php` oder via Config): +- `offers-r`, `offers-w`, `offers-send`, `offers-accept`, `offer-templates-w` + +**Menü-Eintrag** in `resources/views/layouts/_sidenav.blade.php` (analog zu bestehendem „Anfragen" und „Buchungen"). + +**Akzeptanzkriterien:** +- [ ] Routen erreichbar nur mit Permission. +- [ ] Menüpunkt „Angebote" erscheint zwischen „Anfragen" und „Buchungen". + +--- + +### Ticket A7 — Factories + Seeder (für Tests) + +**Typ:** Code +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** A2 + +**Dateien (neu):** +- `database/factories/OfferFactory.php` +- `database/factories/OfferVersionFactory.php` +- `database/factories/OfferItemFactory.php` +- `database/factories/OfferTemplateFactory.php` + +Factories decken alle Status ab (States: `draft()`, `sent()`, `accepted()`). Seeder legt 5 Test-Templates an. + +--- + +## Phase B — Admin-UI + +### Ticket B1 — Offer-Liste `/offers` + +**Typ:** UI + Controller +**Aufwand:** 2 Tage +**Abhängigkeiten:** A6 + +**Dateien (neu):** +- `app/Http/Controllers/OfferController.php` — Methode `index()`, `getOffers()`. +- `resources/views/offer/index.blade.php` +- `resources/views/offer/_list_row.blade.php` (optional, wenn DataTable serverseitig gerendert) + +**Ablauf:** +- DataTable (Server-side via `DataTableController`-Pattern, siehe `data/table/bookings`-Route). +- Spalten: Angebots-Nr. | Kontakt | Reise/Organisation | Version | Status-Badge | Gültig bis | Gesamtpreis | Mitarbeiter | Erstellt am | Aktionen. +- Filter: Status (Multi-Select), Mitarbeiter, Datumsrange (erstellt/gültig bis), Volltext (Kontakt/Nummer). +- Status-Badges (wiederverwendbar via ``): + - `draft` grau, `sent` blau, `accepted` grün, `declined` rot, `expired` gelb, `withdrawn` schwarz. +- Aktionen pro Zeile: Öffnen, PDF, Senden, Duplizieren als neues Angebot, Löschen. +- Toolbar: Button „Neues Angebot" (→ öffnet Modal aus Ticket B2). + +**Akzeptanzkriterien:** +- [ ] Pagination, Sortierung, Filter wie in `/bookings`. +- [ ] Performance: bei 10.000 Angeboten Ladezeit <1s (Index-Check). + +--- + +### Ticket B2 — „Neues Angebot" Modal + Wizard-Einstieg + +**Typ:** UI + Controller +**Aufwand:** 1.5 Tage +**Abhängigkeiten:** B1 + +**Dateien (neu):** +- `resources/views/offer/modal-new-offer.blade.php` +- `resources/views/offer/create.blade.php` (leiterer Wizard-Einstieg) + +**UI-Fluss:** +1. Modal öffnet aus zwei Kontexten: + - Global: `/offers` → Toolbar „Neues Angebot". + - Aus Anfrage: `/inquiry/detail/{id}` → Button „Angebot erstellen" (neu) — pre-fillt Kontakt + Anfrage. +2. Modal-Felder: Kontakt (Autocomplete `/data/table/contacts`), optional Anfrage (Autocomplete Inquiries des Kontakts), Vorlage (Dropdown `offer_templates`). +3. Bei Submit: `POST /offer/action/create` → legt via `OfferService::createFromInquiry()` oder `::createBlank()` an, Redirect auf `/offer/detail/{id}`. + +**Akzeptanzkriterien:** +- [ ] Kontakt-Autocomplete greift auf `ContactController::getContacts`. +- [ ] Vorlage optional (leere Auswahl = leeres Angebot). +- [ ] Nach Submit landet der User direkt im Editor der neuen Version. + +--- + +### Ticket B3 — Offer-Editor (Detail + Versionsansicht) + +**Typ:** UI + Controller +**Aufwand:** 5 Tage (größtes UI-Ticket) +**Abhängigkeiten:** B2 + +**Dateien (neu):** +- `resources/views/offer/detail.blade.php` (Haupt-View, analog zu `booking/detail.blade.php`) +- `resources/views/offer/_detail_header.blade.php` — Status-Leiste, Buttons (Versenden, Kopie als neue Version, In Buchung umwandeln, Löschen) +- `resources/views/offer/_detail_contact.blade.php` — Kontakt-Card (read-only, Link zu `/contact/detail/{id}`) +- `resources/views/offer/_detail_content.blade.php` — Headline + Intro + Itinerary (WYSIWYG) + Closing +- `resources/views/offer/_detail_items.blade.php` — Positions-Liste (Drag&Drop via SortableJS) +- `resources/views/offer/_detail_items_row.blade.php` — einzelne Position +- `resources/views/offer/_detail_versions.blade.php` — History-Tab: V1, V2 … mit PDF-Download je Version + +**Layout (Inspiriert an `booking/detail`):** +``` +┌─ Header: "Angebot 2026-00123 · V2 · Status: GESENDET" ──┐ +│ [PDF] [Erneut senden] [Neue Version] [In Buchung] [...] │ +└─────────────────────────────────────────────────────────┘ +┌─ Linke Spalte (Stammdaten) ──┬─ Rechte Spalte (Tabs) ──┐ +│ • Kontakt │ [Inhalt][Positionen] │ +│ • Anfrage (verknüpft) │ [Dokumente][Versionen] │ +│ • Gültig bis │ [E-Mails][Log] │ +│ • Mitarbeiter │ │ +│ • Summe │ (aktiver Tab-Inhalt) │ +└──────────────────────────────┴─────────────────────────┘ +``` + +**Inhalts-Tab:** +- Felder `headline`, `intro_text`, `itinerary_text` (TinyMCE oder TipTap — gleicher WYSIWYG wie CMS-Modul), `closing_text`. +- Inline-Edit mit Auto-Save (Debounce 3s, `POST /offer/detail/{id}` mit Action-Flag `autosave-content`). + +**Positionen-Tab:** +- Drag&Drop-Sortierung (SortableJS). +- Pro Zeile: Typ-Icon, Titel, Menge, Einzelpreis, Gesamtpreis, Löschen, „Aus Reiseprogramm füllen" (Dropdown aus v2-Programmen, solange v2 läuft: über Lesemodell). +- Summe live unten. +- Button „Position hinzufügen" öffnet kleines Modal (Typ wählen). + +**Bearbeitbarkeitsregel:** +- Wenn `OfferVersion::isEditable()` = false → alle Felder read-only, Hinweis „Diese Version ist versendet. Erstelle eine neue Version, um Änderungen vorzunehmen." mit Button „Neue Version". + +**Akzeptanzkriterien:** +- [ ] Autosave für Content + Items (kein manuelles Speichern nötig). +- [ ] Positionen lassen sich drag&drop verschieben, `position` wird gespeichert. +- [ ] Summe stimmt mit `total_price` in DB überein (Server rechnet nach). +- [ ] Bei gesendeter Version: keine Edit-Möglichkeit, klarer CTA „Neue Version". + +--- + +### Ticket B4 — Dokumente am Angebot (zentral + frei) + +**Typ:** UI + Controller +**Aufwand:** 2 Tage +**Abhängigkeiten:** B3 + +**Entscheidung 17.3:** Dual-Modell wie bei Buchungen. + +**Dateien (neu/geändert):** +- `app/Http/Controllers/OfferFileController.php` (Upload/Delete, analog zu `BookingController::action()` „upload-booking-file") +- `resources/views/offer/_detail_documents.blade.php` +- `resources/views/offer/modal-new-offer-files.blade.php` (Dropzone, nutzt gleiche Blade-Partials wie `booking/modal-new-booking-files`) + +**UI:** +Zwei Bereiche: +1. **Zentral hinterlegte Dokumente** (Checkboxen): + - Liste kommt aus einem neuen Mini-Modell `OfferDocumentTemplate` **oder** aus einer Konfig-Datei (`config/offer_documents.php`). Empfehlung: Konfig-Datei für den Start. + - Beispiele: AGB, Reisebedingungen, Briefbogen Organisation X, Prospekt Reise Y. + - Ausgewählte IDs liegen in `offer_versions.template_document_ids` (JSON). +2. **Freie Uploads pro Version:** + - Dropzone → `OfferFileController::upload($versionId)` → `OfferFileRepository`. + - Liste der hochgeladenen Files mit Löschen-Button. + - Nach Versand: `frozen=true` → Löschen-Button weg. + +**Akzeptanzkriterien:** +- [ ] Upload funktioniert wie Booking-Upload (gleicher Dropzone-Partial wiederverwendet, falls machbar). +- [ ] Gewählte zentrale Dokumente + freie Uploads werden zusammen mit PDF im Versand-Mail-Anhang kombiniert (Ticket B6). +- [ ] Nach Versand: Dokumente read-only, markiert mit „Eingefroren". + +**Nach Phase 4 Modul 3:** Migration `offer_files` → `attachments`-Tabelle, in diesem Ticket aber bewusst auf eigener Tabelle. + +--- + +### Ticket B5 — PDF-Generierung + +**Typ:** Code + Template +**Aufwand:** 2.5 Tage +**Abhängigkeiten:** B3, B4 + +**Dateien (neu):** +- `app/Libraries/CreateOfferPDF.php` (analog zu `CreatePDF.php`) +- `resources/views/pdf/offer.blade.php` (Haupt-Template) +- `resources/views/pdf/offer/header.blade.php`, `footer.blade.php`, `items.blade.php` + +**Logik:** +- PDF wird für eine `OfferVersion` erzeugt (nicht für das übergeordnete `offer`). +- Header: Logo, „Angebot 2026-00123 · Version 2", Kundenadresse, Mitarbeiter, Datum, Gültig bis. +- Sektion „Headline" (falls gesetzt) — fett, groß. +- Sektion „Einführungstext" — `{!! $version->intro_text !!}` (aus WYSIWYG). +- Sektion „Leistungen" — Tabelle der `offer_items`, gruppiert nach Typ, Summe unten. +- Sektion „Reiseverlauf" — nur wenn `itinerary_text` gefüllt. +- Sektion „Abschlusstext". +- Footer: Firmenadresse, Seite X/Y, Angebots-Nr. + +**PDF-Mergen mit Anhängen:** +- `MyPDFMerger` (existiert bereits) mergt erzeugtes PDF mit ausgewählten zentralen Dokumenten (die auf Disk liegen). +- Endergebnis: ein einziges PDF-File, Pfad in `offer_versions.pdf_path`. + +**Route:** `GET /offer/pdf/{versionId}` streamt das erzeugte PDF (Download oder Inline via Query `?inline=1`). + +**Akzeptanzkriterien:** +- [ ] PDF für eine Beispiel-Version erzeugt sich korrekt, sieht wie Buchungsauftrag aus, nur mit „Angebot". +- [ ] Merger funktioniert: Angebots-PDF + 2 zentrale Dokumente = 1 PDF. +- [ ] Nach erfolgreicher Erzeugung wird `pdf_path` gespeichert; erneutes Abrufen nutzt Cache. +- [ ] Bei Status-Wechsel (neue Version) wird altes PDF nicht überschrieben. + +--- + +### Ticket B6 — Versand via E-Mail + +**Typ:** Code + UI +**Aufwand:** 2 Tage +**Abhängigkeiten:** B5, Modul 7 (idealerweise Draft-Mail-Modal vorhanden, sonst eigenes Modal) + +**Dateien (neu/geändert):** +- `app/Mail/OfferMail.php` (Laravel Mailable) +- `resources/views/emails/offer/sent.blade.php` (HTML-Template) +- `resources/views/offer/modal-send-offer.blade.php` — Vorschau-Modal vor Versand (Empfänger, Betreff, Body bearbeitbar, Anhänge-Liste). +- `OfferController::action('send', $id)` → delegiert an `OfferService::send()`. + +**Logik:** +1. User klickt „Versenden" → Modal mit vorbefülltem Betreff (`"Ihr persönliches Angebot von Sterntours — {offer_number}"`) und Body (aus CMS-Textvorlage, Platzhalter `{name}`, `{offer_number}`, `{link}`). +2. User kann Betreff/Body anpassen. +3. `OfferService::send()`: + - Erzeugt neuen `OfferAccessToken`. + - Ersetzt Platzhalter `{link}` durch die öffentliche URL aus Ticket D1. + - Baut Mail mit PDF (aus B5) + freien Anhängen (aus B4) + zentralen Dokumenten. + - Versendet per Queue (`Mail::to($contact->email)->queue(new OfferMail(...))`). + - Erzeugt Eintrag in `communications` (nach Modul 3 Phase 4) bzw. `customer_mails` (Fallback solange Phase 4 offen). + - Setzt `offer_versions.status=sent`, `sent_at`, `pdf_archived=false`. + - Setzt `offers.status=sent`. + - Setzt `frozen=true` auf `offer_files` der Version. + +**Akzeptanzkriterien:** +- [ ] Mail landet bei Empfänger mit PDF + Link. +- [ ] Token im Link ist einzigartig und nicht erratbar (≥ 48 Zeichen Entropie). +- [ ] Versand wird im Kommunikations-Verlauf des Kontakts sichtbar. +- [ ] Bei Queue-Fehler → Status bleibt `draft`, Fehler im Dashboard anzeigbar. + +--- + +### Ticket B7 — Admin-Annahme/Ablehnung/Zurücknahme + +**Typ:** UI + Controller +**Aufwand:** 1 Tag +**Abhängigkeiten:** B6 + +**Dateien (neu):** +- `resources/views/offer/modal-admin-accept.blade.php` +- `resources/views/offer/modal-admin-decline.blade.php` + +**UI:** +- Im Detail-Header (Ticket B3) Buttons „Zusage markieren", „Absage markieren", „Angebot zurückziehen". +- Bei Zusage: Modal fragt nach Notiz (optional), ruft `OfferService::markAccepted($v, 'admin')` auf. +- Bei Absage: Modal fragt nach Grund (optional), ruft `markDeclined($v, 'admin')`. +- Zurückziehen: `OfferService::withdraw()`; invalidiert Token, setzt Offer.status=withdrawn. + +**Akzeptanzkriterien:** +- [ ] Zusage/Absage-Events werden in `notices`/Audit-Log festgehalten. +- [ ] Button „In Buchung übernehmen" wird erst nach Zusage aktiv. + +--- + +### Ticket B8 — Angebot → Buchung konvertieren + +**Typ:** Code + UI +**Aufwand:** 2 Tage +**Abhängigkeiten:** B7, **Modul 4 (BookingService::createManual)** + +**Dateien (neu/geändert):** +- `app/Services/OfferService.php` — `convertToBooking()` fertigstellen. +- `resources/views/offer/modal-convert-to-booking.blade.php` — Bestätigungs-Modal mit Vorschau der Buchungsdaten. + +**Logik:** +- `convertToBooking(Offer $offer): Booking`: + 1. Guard: Offer.status=accepted, Offer.booking_id IS NULL. + 2. Baut `$bookingData` aus aktueller OfferVersion (Items → BookingServiceItems, Summe → BookingPrice, Kontakt → Customer, Reisedatum → aus Items oder aus Offer-Meta). + 3. Ruft `BookingService::createManual($bookingData, $offer->inquiry)` auf (Modul 4). + 4. Kopiert `offer_files` (frozen) als `booking_files`. + 5. Setzt `offer.booking_id`, persistiert. + 6. Legt Notiz an: „Erzeugt aus Angebot 2026-00123 V2". + +**Akzeptanzkriterien:** +- [ ] Nach Konvertierung ist in der Buchung alles sichtbar: Kontakt, Positionen, Dokumente. +- [ ] Angebot bleibt erhalten, verlinkt auf neue Buchung; Status bleibt `accepted`. +- [ ] Idempotent: zweiter Aufruf schlägt mit klarer Fehlermeldung fehl. + +--- + +### Ticket B9 — Offer-Löschen + Soft Delete + +**Typ:** Code + UI +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** B1 + +**Dateien (geändert):** +- `OfferController::delete()`, Confirm-Modal `offer/modal-delete.blade.php`. + +**Regeln:** +- Soft-Delete nur möglich, wenn Offer.booking_id IS NULL. +- Sonst Error-Toast „Dieses Angebot ist mit Buchung {id} verknüpft und kann nicht gelöscht werden. Bitte zuerst Buchung entfernen." +- Alternativ Status `withdrawn` wählen. + +--- + +## Phase C — Vorlagen (Offer-Templates) + +### Ticket C1 — Template-Liste + Editor + +**Typ:** UI + Controller +**Aufwand:** 1.5 Tage +**Abhängigkeiten:** A3, A6 + +**Dateien (neu):** +- `app/Http/Controllers/OfferTemplateController.php` +- `resources/views/offer_template/index.blade.php` +- `resources/views/offer_template/detail.blade.php` + +**Felder identisch zu OfferVersion (ohne Kontakt/Summe)**. Positionen werden in `items_json` gespeichert. + +### Ticket C2 — „Als Vorlage speichern" aus Angebot + +**Typ:** Code + UI +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** C1, B3 + +- Button im Offer-Header „Als Vorlage speichern" → öffnet Modal (Name, Organisation-Auswahl) → `OfferTemplateRepository::createFromVersion($v, $name, $org)`. + +### Ticket C3 — Gruppierung nach Reise-Organisation + +**Typ:** UI +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** C1 + +- Dropdown im B2-Modal gruppiert Vorlagen nach `organization`. + +--- + +## Phase D — Kundenseitiger Freigabe-Link + +### Ticket D1 — Öffentliche Route auf sterntours.de + +**Typ:** Code (Symfony oder Laravel — Entscheidung unten) +**Aufwand:** 1 Tag +**Abhängigkeiten:** B6 + +**Entscheidung:** Die öffentliche Seite läuft auf **`mein.sterntours.de/angebot/{token}`** (Laravel), nicht im Symfony-Frontend. Begründung: Token-Handling und Laravel-Auth-Scaffolding liegen ohnehin in Laravel, doppelter Frontend-Bau im Legacy-System wäre Mehraufwand. URL kann per Subdomain `angebote.sterntours.de` gebrandet werden (Traefik-Route). + +**Dateien (neu):** +- `routes/web.php` — neuer Block ohne `auth.2fa`-Middleware: +```php +Route::group(['middleware' => ['web']], function () { + Route::get('/angebot/{token}', 'Public\OfferAccessController@show')->name('public_offer_show'); + Route::post('/angebot/{token}/accept', 'Public\OfferAccessController@accept')->name('public_offer_accept'); + Route::post('/angebot/{token}/decline','Public\OfferAccessController@decline')->name('public_offer_decline'); +}); +``` +- `app/Http/Controllers/Public/OfferAccessController.php` +- `app/Http/Middleware/ValidOfferToken.php` (prüft Existenz, expiry; 404 sonst; setzt `$request->attributes->set('offerVersion', …)`) + +**Akzeptanzkriterien:** +- [ ] Ungültiger/abgelaufener Token → 404 mit freundlicher Fehlerseite. +- [ ] Gültiger Token → `opened_at` wird beim ersten Aufruf gesetzt. + +### Ticket D2 — Kunden-Ansichtsseite + +**Typ:** UI +**Aufwand:** 1.5 Tage +**Abhängigkeiten:** D1 + +**Dateien (neu):** +- `resources/views/public/offer/show.blade.php` — eigenes, schlankes Layout (nicht Admin-Sidebar). +- `resources/views/public/offer/_summary.blade.php` +- `resources/views/public/offer/_actions.blade.php` +- `resources/views/public/offer/accepted.blade.php` (Danke-Seite nach Annahme) +- `resources/views/public/offer/declined.blade.php` + +**Inhalt:** +- Begrüßung („Guten Tag Frau Musterfrau"). +- PDF-Embed (iframe oder viewer.js). +- Download-Button PDF. +- Anhänge-Liste zum Download. +- Buttons „Angebot annehmen" / „Angebot ablehnen" / „Ich habe Fragen". +- Bei Annahme: Bestätigungs-Dialog („Sind Sie sicher? Mit Ihrer Bestätigung erfolgt die verbindliche Annahme."), dann POST. + +### Ticket D3 — Annahme-/Ablehnungs-Flow + +**Typ:** Code +**Aufwand:** 1 Tag +**Abhängigkeiten:** D2 + +- `OfferAccessController::accept()`: prüft Token, ruft `OfferService::markAccepted($v, 'customer_link', $ip, $ua)`, sendet interne Benachrichtigung an zuständigen Mitarbeiter (Mail + Dashboard-Notice). +- `OfferAccessController::decline()`: optionales Grund-Feld, `markDeclined()`. +- Token wird nach Annahme/Ablehnung invalidiert (`accepted_at`/`declined_at` gesetzt, erneuter Aufruf zeigt Danke-Seite mit Historie). + +**Akzeptanzkriterien:** +- [ ] Nach Annahme erhält Mitarbeiter eine Mail + Dashboard-Notiz. +- [ ] Kunde kann bei wiederholtem Öffnen des Links die Danke-Seite sehen, aber nicht erneut annehmen. +- [ ] IP/User-Agent werden protokolliert. + +### Ticket D4 — „Ich habe Fragen"-Kanal + +**Typ:** Code + UI +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** D2 + +- Button öffnet Modal mit Textarea. +- Submit legt neue `communication` an (oder bis Modul 3 Phase 4 fertig: `lead_mail`/`customer_mail`) mit Richtung „eingehend", `from=contact.email`, verknüpft mit Offer. + +### Ticket D5 — Rate-Limiting + CSRF-Tokens + +**Typ:** Code +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** D1 + +- `RateLimiter::for('offer-access', …)` — max 30 Requests/Minute pro IP. +- CSRF bleibt an (public Route in `web`-Group). +- Token-Rate-Limit: max 5 falsche Token-Versuche/IP/Minute (schützt vor Brute-Force). + +--- + +## Phase E — Versionierung & Archivierung + +### Ticket E1 — „Neue Version" Flow + +**Typ:** Code + UI +**Aufwand:** 1 Tag +**Abhängigkeiten:** B6, A4 + +- Button „Neue Version" im Detail-Header einer gesendeten Version. +- `OfferService::supersede()` → `OfferVersionRepository::createNewVersion()` (A3). +- UI lädt direkt die neue Version zum Editieren. +- Alte Version bleibt sichtbar im Versions-Tab (Ticket B3 `_detail_versions.blade.php`), als Read-only, mit Badge „Überholt". + +### Ticket E2 — Versions-Historie im Detail + +**Typ:** UI +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** B3, E1 + +- Versions-Tab zeigt chronologische Liste aller `OfferVersion`s mit: + - Version-Nr., Status-Badge, Erstellt am, Versendet am, Summe, PDF-Link. + - Link „Diese Version ansehen" (read-only). + +### Ticket E3 — PDF-Archivierung Cron + +**Typ:** Code +**Aufwand:** 1 Tag +**Abhängigkeiten:** B5 + +**Dateien (neu):** +- `app/Console/Commands/OfferArchiveOldPdfs.php` + +**Logik:** +- Läuft nächtlich via `Console\Kernel::schedule()`. +- Für jede Offer: PDFs aller Versionen außer der aktuellen + der zuletzt akzeptierten werden auf kalten Storage (`disk('offers-cold')`) verschoben, `pdf_archived=true`. +- Konfigurierbar via `config/offers.php` (Retention-Limit, Disk-Name). + +**Akzeptanzkriterien:** +- [ ] PDF-Abruf (Ticket B5 Route) generiert das PDF bei Bedarf neu, wenn `pdf_archived=true` und Datei nicht auf Hot-Disk. +- [ ] Konfiguration über `config/offers.php` steuerbar. + +--- + +## Phase F — Polish, Automatik, Tests + +### Ticket F1 — Zähler/Badges in Contacts und Inquiries + +**Typ:** UI + Code +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** B1 + +- In `resources/views/contact/index.blade.php` neue Spalte „Angebote" mit Zähler-Badge (analog Anfragen/Buchungen aus Phase 1 Contacts). +- Klick öffnet History-Modal (analog `_detail_history.blade.php`, Scope auf Offers). +- Dito in `resources/views/lead/detail.blade.php` / `inquiry/detail.blade.php`: neue Sektion „Angebote zu dieser Anfrage". + +### Ticket F2 — Ablauf-Automatik (expired-Cron) + +**Typ:** Code +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** A4 + +**Dateien (neu):** +- `app/Console/Commands/OfferExpireVersions.php` + +**Logik:** +- Täglich um 03:00: setzt alle `OfferVersion` mit `valid_until < today` und `status=sent` auf `expired`. +- Wenn Offer keine aktiven Versionen mehr hat → Offer.status=expired. +- Optional: Erinnerung 3 Tage vor Ablauf an zuständigen Mitarbeiter (Feature-Flag). + +### Ticket F3 — Communications-Integration + +**Typ:** Code +**Aufwand:** 1 Tag +**Abhängigkeiten:** Modul 3 Phase 4 (nach Abschluss), B6, D4 + +- Offer-bezogene E-Mails werden mit `offer_id` (und `offer_version_id`) in `communications` hinterlegt. +- Detail-View Ticket B3 bekommt eigenen Tab „E-Mails" mit den zugehörigen Nachrichten. +- Wenn Kunde über Freigabe-Link antwortet (D4): Notification landet ebenfalls im gleichen Verlauf. + +### Ticket F4 — Feature-Tests + +**Typ:** Tests +**Aufwand:** 2 Tage +**Abhängigkeiten:** alle + +**Dateien (neu):** +- `tests/Feature/Offer/OfferCrudTest.php` +- `tests/Feature/Offer/OfferWorkflowTest.php` (draft → sent → accepted → converted) +- `tests/Feature/Offer/OfferVersioningTest.php` (supersede, history) +- `tests/Feature/Offer/OfferPublicAccessTest.php` (Token-Flow) +- `tests/Feature/Offer/OfferPdfTest.php` (PDF wird erzeugt, enthält Angebots-Nr.) +- `tests/Unit/Services/OfferServiceTest.php` + +**Coverage-Ziel:** ≥ 80 % für `OfferService` + `OfferRepository`. + +### Ticket F5 — Dokumentation + +**Typ:** Doku +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** alle + +- `dev/offers/README.md` mit Funktionsübersicht. +- `dev/offers/user-guide.md` — Anleitung für Mitarbeiter (mit Screenshots). +- Eintrag in `CLAUDE.md` / `mein.sterntours.de/CLAUDE.md` für künftige AI-Assistenz. + +### Ticket F6 — Permissions-Review + Seeder-Update + +**Typ:** Code +**Aufwand:** 0.5 Tage +**Abhängigkeiten:** A6 + +- Seeder legt Permissions `offers-r`, `offers-w`, `offers-send`, `offers-accept`, `offer-templates-w` an. +- Bestehende Rollen (Admin, Mitarbeiter, Buchhaltung) werden mit sinnvoller Default-Zuweisung versehen. + +--- + +## 2. Umsetzungs-Reihenfolge (Sprint-Vorschlag, 2-Wochen-Sprints) + +| Sprint | Tickets | Ziel | +|--------|---------|------| +| S1 | A1–A7 | Fundament steht, leere Offer-Liste im Backend erreichbar | +| S2 | B1–B3 | Angebot anlegen & bearbeiten (ohne Versand) | +| S3 | B4–B6 | Dokumente + PDF + Versand funktionieren | +| S4 | B7–B9, E1, E2 | Annahme/Ablehnung, Versionierung | +| S5 | C1–C3, D1–D5 | Vorlagen-Verwaltung + Kundenseite (Parallel möglich) | +| S6 | E3, F1–F6, B8 | Archivierung, Integration in Contacts/Inquiries, Angebot→Buchung, Tests, Doku | + +**Meilenstein „MVP verwendbar" nach S3**: Mitarbeiter können ein Angebot anlegen, PDF erzeugen und versenden — Annahme zunächst nur per Admin-Statuswechsel. + +**Meilenstein „Vollversion" nach S6**: Inklusive Kundenportal-Link, Versionierung, Archivierung, Konvertierung in Buchung, Tests. + +--- + +## 3. Risiken und Gegenmaßnahmen + +| # | Risiko | Wahrscheinlichkeit | Wirkung | Gegenmaßnahme | +|---|--------|--------------------|---------|----------------| +| R1 | `BookingService::createManual()` aus Modul 4 noch nicht fertig, wenn B8 dran ist | Mittel | B8 blockiert | Mocking in S4, echte Integration in S6 | +| R2 | PDF-Generierung performance-kritisch bei vielen Anhängen (PDFMerger) | Niedrig | PDF-Erstellung dauert > 10 s | Queue-basierte Erzeugung (async Job), UI zeigt „Wird erstellt …" | +| R3 | Token-URLs werden öffentlich geteilt (Social Media etc.) | Mittel | Unerwünschter Einblick | Token-Expiry 14 Tage Standard, Viewer-Log zeigt auffälliges Öffnen | +| R4 | `travel_program_id` in `offer_items` zeigt auf v2-Daten, die wegmigrieren | Hoch | Datenbezüge brechen nach v2-Migration | Metadata speichert Titel/Preis zum Zeitpunkt der Erstellung — funktioniert auch bei fehlendem FK | +| R5 | Mitarbeiter bearbeiten ein gesendetes Angebot unbewusst (Auto-Save) | Mittel | Inkonsistente Versionen | Auto-Save nur bei `isEditable()==true`, sonst Felder disabled | +| R6 | Konflikt mit Modul 3 Phase 4 (attachments) | Hoch | Doppelte Speicherorte | Eigene `offer_files`-Tabelle jetzt, Migration in `attachments` ist eigenes Ticket nach Phase 4 — **bewusst so geplant** | + +--- + +## 4. Offene Detail-Entscheidungen (können während Umsetzung geklärt werden) + +1. **Angebots-Nummernkreis:** `{YYYY}-{5-stellig}` oder durchlaufend ohne Jahr? → Vorschlag: mit Jahr (leichter zu sortieren, klassisches Format). +2. **Subdomain für Kundenseite:** `/angebot/{token}` auf `mein.sterntours.de` (einfacher) oder `angebote.sterntours.de` (professioneller für Kunden)? → Vorschlag: Subdomain, da Kunden nicht den Admin-Hostnamen sehen sollten. Technisch ist es dieselbe Laravel-App. +3. **WYSIWYG-Editor:** TinyMCE (bereits im System für CMS) oder TipTap (moderner, lizenzfrei)? → Vorschlag: TinyMCE, da schon integriert. +4. **PDF-Engine:** TCPDF / DomPDF (beide im System, siehe `app/Libraries/`) oder weiterer Kandidat (wkhtmltopdf)? → Vorschlag: DomPDF, da bereits für Booking-PDFs genutzt; konsistente Optik. +5. **Token-Gültigkeit Default:** 14 Tage, 30 Tage, bis `valid_until`? → Vorschlag: bis `valid_until + 7 Tage` (Kunde soll nach Ablaufdatum noch die Ablehnungs-Bestätigung sehen können). + +Diese 5 Punkte würde ich Ende S1 (nach Fundament) kurz final abstimmen, damit S2 sauber losfahren kann. + +--- + +## 5. Abnahmeliste (Gesamt-Abschluss Modul 6) + +- [ ] Mitarbeiter kann aus Anfrage heraus in < 2 Minuten ein Angebot mit Vorlage erstellen. +- [ ] Mitarbeiter kann PDF generieren und ansehen. +- [ ] Mitarbeiter kann Angebot per E-Mail versenden; Kunde erhält PDF + Link. +- [ ] Kunde kann über Link das Angebot ansehen und mit einem Klick annehmen oder ablehnen. +- [ ] Mitarbeiter sieht Annahme im Dashboard und bekommt Benachrichtigung. +- [ ] Aus angenommenem Angebot kann mit einem Klick eine Buchung erzeugt werden. +- [ ] Nach Versand ist das Angebot read-only; Änderungen erzeugen eine neue Version. +- [ ] Alte Versionen bleiben auffindbar und ihr PDF weiterhin downloadbar. +- [ ] In Kontakt- und Anfrage-Ansicht ist die Anzahl der Angebote sichtbar und klickbar. +- [ ] Abgelaufene Angebote werden automatisch auf `expired` gesetzt. +- [ ] Alle neuen Routen haben Permission-Checks. +- [ ] Feature-Tests grün, Unit-Test-Coverage ≥ 80 % im Service/Repository. +- [ ] Dokumentation für Entwickler (`dev/offers/README.md`) und für Mitarbeiter (`user-guide.md`) vorhanden. diff --git a/dev/projekt-empfehlungen-2026-04.md b/dev/projekt-empfehlungen-2026-04.md new file mode 100644 index 0000000..31ae141 --- /dev/null +++ b/dev/projekt-empfehlungen-2026-04.md @@ -0,0 +1,138 @@ +# Projekt-Analyse: sinnvolle Erweiterungen und Optimierungen (mein.sterntours.de) + +Stand: April 2026. Dieses Dokument ergänzt und aktualisiert die frühere Übersicht in [`audit-april-2025.md`](./audit-april-2025.md) und fasst zusätzliche Beobachtungen aus Code-Struktur, Abhängigkeiten und typischen Laravel-Betriebsmustern zusammen. + +--- + +## Kurzfassung + +Das CRM ist ein ausgereiftes **Laravel-10**-Monolith mit klarer Schichtung (Controller → Request/Repository → Services/Models), **Zwei-Datenbank-Setup** (CRM + read-only Legacy) und **Passport** für API-Bereiche. Die größten Hebel für die nächsten Monate sind: **Wartbarkeit** (Duplikate reduzieren, moderne Laravel-Konventionen), **Sicherheit** (öffentliche API-Endpunkte härten), **Qualität** (Tests, statische Analyse), **Frontend-Tooling** (langfristig von Laravel Mix 2 weg) und **Betrieb** (Scheduler, Monitoring, asynchrone Mail). + +--- + +## Abgleich mit dem Audit April 2025 + +| Thema | Früher (Audit) | Aktueller Stand (Code-Review) | +|--------|------------------|-------------------------------| +| API-Key Booking-Import | Hardcoded im Controller | **Erledigt im Sinne von Konfiguration:** `API/BookingController` nutzt `config('app.success_key')` → `env('SUCCESS_KEY')` in `config/app.php`. Sicherstellen, dass `.env`/`SUCCESS_KEY` in allen Umgebungen gesetzt und **nicht** ins Repo gelangt. | +| PHPUnit / SQLite zweite DB | `mysql_stern` fehlt in Tests | **Weiterhin relevant:** In `phpunit.xml` sind keine `DB_*`-Variablen für die Stern-Verbindung gesetzt; Tests mit Legacy-Models bleiben erschwert. | +| Frontend (Mix 2, node-sass) | Veraltet | **Unverändert:** `package.json` mit Laravel Mix **2.1** und sehr altem Ökosystem – größeres Migrationsprojekt. | +| Code-Duplizierung Booking/Lead (Mail-Verzeichnisse) | Offen | **Weiterhin sinnvoll:** Gemeinsamen Service/Trait extrahieren. | + +--- + +## 1. Architektur und Wartbarkeit + +### 1.1 Gemeinsame Logik zusammenführen + +- **Mail-Verzeichnis-Logik** in `Services/Booking.php` und `Services/Lead.php` (siehe Audit): Ziel sollte ein klar benannter **`MailDirService`** (oder Trait nur, wenn wirklich nur technische Wiederholung) sein, inklusive Tests. +- **Parallele Mail-Repositories** (`CustomerMailRepository`, `LeadMailRepository`, `CustomerFewoMailRepository`): `sendMail`/`prepareMessageFull` sind sich sehr ähnlich – mittelfristig Basis-Klasse oder gemeinsames **„Mail senden mit Anhängen/Fehlerstatus“**-Modul prüfen (ohne Big-Bang-Refactoring). + +### 1.2 Laravel-Konventionen und Typisierung + +- Controller-Methoden mit **`Illuminate\Http\Request` injizieren** statt `\Request::all()` (globale Facade), wo möglich – verbessert Testbarkeit und IDE-Unterstützung. +- **`BaseRepository` und ältere Services** schrittweise mit **Return-Types** und strengeren Parametertypen versehen (Audit erwähnt das bereits). +- API-Routen nutzen noch **String-Controller-Referenzen** (`'API\BookingController@import'`). Migration auf **Klassen-Syntax** (`[BookingController::class, 'import']`) erleichtert spätere Laravel-11/12-Upgrades und Refactoring-Tools. + +### 1.3 Offene TODOs gezielt abarbeiten + +Im Projekt existieren zahlreiche `//TODO`-Marker (u. a. CMS, Settings „linked“-Prüfungen, Mail-Modals „load subdirs by pos id“, IQ-ContentTree). Sinnvoll ist ein **kleines Backlog** nach Kategorie (Bug, UX-Schuld, Sicherheit) – nicht alles auf einmal, aber sichtbar machen, damit keine stillen Lücken bleiben. + +--- + +## 2. Sicherheit + +### 2.1 Öffentliche API-Endpunkte + +- **`POST /api/navigation/cache/clear`** ist ohne `auth:api` definiert. Jeder kann damit theoretisch den Navigations-Cache leeren (DoS/Cache-Störung). **Empfehlung:** Secret-Header, IP-Allowlist, oder **nur authentifizierte** Aufrufer (je nach Aufrufer-Architektur). +- **`booking/import`** akzeptiert laut Routen **GET und POST**. GET-Requests mit Seiteneffekten (Import) sind problematisch (Logs, Proxies, prefetch). **Empfehlung:** nur **POST**, dokumentieren; Legacy-GET nur temporär mit Deprecation-Header/Log. +- Booking-Import: Neben dem Shared Secret **Rate-Limiting** prüfen (eigener `RateLimiter::for('booking-import', …)`), falls der Endpunkt von wenigen festen IPs kommt: zusätzlich **IP-Bindung** in Middleware. + +### 2.2 API allgemein + +- Öffentliche Routen (`cms/*`, `passolution`, `navigation/*`) sollten einmal **inventarisiert** werden: Welche liefern personenbezogene oder interne Daten? Braucht es **API-Versionierung** (`/api/v1/...`) für externe Konsumenten? + +### 2.3 Abhängigkeiten + +- In `composer.json` stehen mehrere Pakete mit **`"*"`** (z. B. `iqcontent/laravel-filemanager`, `jenssegers/date`, …). **Empfehlung:** auf **konkrete Versionen** pinnen, um reproduzierbare Builds und Sicherheits-Updates zu gewährleisten. + +--- + +## 3. Performance und Skalierung + +### 3.1 E-Mail synchron im Request + +Versand erfolgt in Repositories per `Mail::…->send()` im gleichen Prozess wie der HTTP-Request. Unter Last verlängert das Antwortzeiten. + +- **Kurz:** `Mail::queue()` bzw. **Queued Mailables** / **`ShouldQueue`** für nicht-kritische Benachrichtigungen. +- **Voraussetzung:** zuverlässiger **Queue-Worker** (Supervisor/Docker) und Monitoring fehlgeschlagener Jobs. + +### 3.2 Scheduler + +`app/Console/Kernel.php` enthält einen **leeren** `schedule()`-Block. Wenn Cron-Jobs extern oder in anderer Form laufen, **dokumentieren**; sonst wiederkehrende Tasks (Newsletter-Sync, Bereinigungen, Cache-Warmup) **hier** oder als dokumentierte `artisan`-Crons abbilden – sonst droht „vergessene“ Automatisierung bei Serverwechsel. + +### 3.3 Caching + +- Navigation nutzt bereits Caching im Service – gut. **Strategie:** TTLs, Cache-Tags (falls Redis/Memcached), und Invalidierung nur über vertrauenswürdige Wege (siehe 2.1). + +--- + +## 4. Qualität, Tests und Entwicklererfahrung + +### 4.1 Testabdeckung + +Aktuell ein **kleines Set** Feature/Unit-Tests (siehe Audit-Liste). Sinnvolle nächste Schritte: + +- **Factories** für zentrale Domänenobjekte (`Customer`, `Booking`, `Lead`) – Audit nennt das bereits. +- **Zweite DB in PHPUnit** konfigurieren (Audit-Vorschlag mit SQLite zweiter Connection), damit Legacy-Models überhaupt testbar werden. +- Kritische **API-Verträge** (Booking-Import, Fewo-API) mit **Contract-Tests** absichern. + +### 4.2 Statische Analyse und Formatierung + +- **Laravel Pint** (oder PHP-CS-Fixer) für einheitlichen Style – aktuell keine `pint.json` im Projektroot sichtbar. +- **PHPStan/Larastan** (Level schrittweise erhöhen) für frühe Fehler in Repositories und Services. + +### 4.3 Observability + +- Zentral **strukturiertes Logging** für API-Fehler (ohne Secrets), optional eigener Log-Channel für `booking/import`. +- Langfristig: **Laravel Pulse** oder APM (wenn nicht schon vorhanden) für langsame Requests und Queue-Latenzen. + +--- + +## 5. Frontend und Assets + +Unverändert zur Einschätzung im Audit: + +- **Laravel Mix 2**, **Bootstrap 4**, **node-sass** – technisch am Ende der Fahnenstange. +- **Sinnvolle Richtung:** Migration auf **Vite** + **sass (dart-sass)** + schrittweise **Bootstrap 5** oder gezieltes Design-System – als **eigenes Projekt** mit Pilot-View und Build-Pipeline in CI. + +--- + +## 6. Framework- und PHP-Roadmap + +- **Laravel 10** ist LTS-fähig; mittelfristig **Upgrade-Pfad zu Laravel 11** planen (Routing, `bootstrap/app.php`, strukturelle Änderungen). +- **`jenssegers/date`** ist überflüssig, wenn überall **Carbon** genutzt wird – Dependency reduzieren. +- **PHP:** `composer.json` erlaubt 8.1–8.3; einheitliche **8.2/8.3**-Zielversion in CI und Docker festlegen. + +--- + +## 7. Priorisierte Maßnahmen (Vorschlag) + +| Priorität | Maßnahme | Aufwand | +|-----------|----------|---------| +| Hoch | `navigation/cache/clear` absichern (Auth/Secret/IP) | Klein–mittel | +| Hoch | `booking/import`: nur POST, Rate-Limit; Aufrufer-Doku | Klein | +| Hoch | Composer-`**`-Versionen durch feste Versionen ersetzen | Mittel | +| Mittel | `MailDirService` / Duplikate Booking–Lead | Mittel | +| Mittel | Mail-Versand für geeignete Fälle über Queue | Mittel–groß | +| Mittel | PHPUnit: zweite DB + mehr Factories | Mittel | +| Mittel | Pint + Larastan einführen (CI) | Mittel | +| Niedrig | API auf Klassen-Routen, Request-Injection ausbreiten | Laufend | +| Strategisch | Frontend: Vite + Tooling-Modernisierung | Groß | + +--- + +## Referenz + +- Bestehende Detail-Audits und Test-Kommandos: [`audit-april-2025.md`](./audit-april-2025.md) +- UI-/Navigations-Kontext: [`frontend-navigation/BACKEND-UI.md`](./frontend-navigation/BACKEND-UI.md) diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index a51906b..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,159 +0,0 @@ -services: - laravel.test: - build: - context: ./vendor/laravel/sail/runtimes/8.3 - dockerfile: Dockerfile - args: - WWWGROUP: '${WWWGROUP:-20}' - WWWUSER: '${WWWUSER:-501}' - image: sail-8.3/app - extra_hosts: - - 'host.docker.internal:host-gateway' - - environment: - WWWUSER: '${WWWUSER:-501}' - WWWGROUP: '${WWWGROUP:-20}' - LARAVEL_SAIL: 1 - XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' - XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' - IGNITION_LOCAL_SITES_PATH: '${PWD}' - DB_CONNECTION: mysql - DB_HOST: mysql - DB_PORT: 3306 - DB_DATABASE: stern_crm - DB_USERNAME: sail - DB_PASSWORD: password - DB_CONNECTION_STERN: mysql - DB_HOST_STERN: mysql-stern - DB_PORT_STERN: 3306 - DB_DATABASE_STERN: stern_db - DB_USERNAME_STERN: sail - DB_PASSWORD_STERN: password - MAIL_HOST: mailpit - MAIL_PORT: 1025 - REDIS_HOST: redis - volumes: - - '.:/var/www/html' - networks: - - sail - - proxy - depends_on: - - mysql - - mysql-stern - - redis - - mailpit - labels: - - "traefik.enable=true" - # Hauptdomain - - "traefik.http.routers.meinsterntours.rule=Host(`mein.sterntours.test`)" - - "traefik.http.routers.meinsterntours.entrypoints=websecure" - - "traefik.http.routers.meinsterntours.tls=true" - - "traefik.http.routers.meinsterntours.service=sterntours-service" - - # Hauptdomain - - "traefik.http.routers.sterntours.rule=Host(`sterntours.test`)" - - "traefik.http.routers.sterntours.entrypoints=websecure" - - "traefik.http.routers.sterntours.tls=true" - - "traefik.http.routers.sterntours.service=sterntours-service" - - # Asset Domain für Vite-Server (Port 5173 - Haupt/Portal) - - "traefik.http.routers.assets-sterntours.rule=Host(`assets.sterntours.test`)" - - "traefik.http.routers.assets-sterntours.entrypoints=websecure" - - "traefik.http.routers.assets-sterntours.tls=true" - - "traefik.http.routers.assets-sterntours.service=assets-sterntours-service" - - # Service Definition - NUR EINMAL! - - "traefik.http.services.sterntours-service.loadbalancer.server.port=80" - - "traefik.http.services.assets-sterntours-service.loadbalancer.server.port=5173" - - "traefik.http.services.assets-sterntours-service.loadbalancer.server.scheme=http" - - "traefik.docker.network=proxy" - mysql: - image: 'mysql/mysql-server:8.0' - ports: - - '${FORWARD_DB_PORT:-33064}:3306' - environment: - MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' - MYSQL_ROOT_HOST: '%' - MYSQL_DATABASE: '${DB_DATABASE}' - MYSQL_USER: '${DB_USERNAME}' - MYSQL_PASSWORD: '${DB_PASSWORD}' - MYSQL_ALLOW_EMPTY_PASSWORD: 1 - volumes: - - 'sail-mysql:/var/lib/mysql' - - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' - networks: - - sail - healthcheck: - test: - - CMD - - mysqladmin - - ping - - '-p${DB_PASSWORD}' - retries: 3 - timeout: 5s - mysql-stern: - image: 'mysql/mysql-server:8.0' - ports: - - '${FORWARD_DB_PORT_STERN:-33065}:3306' - environment: - MYSQL_ROOT_PASSWORD: '${DB_PASSWORD_STERN}' - MYSQL_ROOT_HOST: '%' - MYSQL_DATABASE: '${DB_DATABASE_STERN}' - MYSQL_USER: '${DB_USERNAME_STERN}' - MYSQL_PASSWORD: '${DB_PASSWORD_STERN}' - MYSQL_ALLOW_EMPTY_PASSWORD: 1 - volumes: - - 'sail-mysql-stern:/var/lib/mysql' - - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' - networks: - - sail - healthcheck: - test: - - CMD - - mysqladmin - - ping - - '-p${DB_PASSWORD_STERN}' - retries: 3 - timeout: 5s - redis: - image: 'redis:alpine' - ports: - - '${FORWARD_REDIS_PORT:-6379}:6379' - volumes: - - 'sail-redis:/data' - networks: - - sail - healthcheck: - test: - - CMD - - redis-cli - - ping - retries: 3 - timeout: 5s - mailpit: - image: 'axllent/mailpit:latest' - ports: - - '${FORWARD_MAILPIT_PORT:-1030}:1025' - - '${FORWARD_MAILPIT_DASHBOARD_PORT:-8030}:8025' - networks: - - sail - - proxy - labels: - - "traefik.enable=true" - - "traefik.http.routers.sterntours-mail.rule=Host(`sterntours-mail.test`)" - - "traefik.http.routers.sterntours-mail.entrypoints=websecure" - - "traefik.http.routers.sterntours-mail.tls=true" - - "traefik.http.services.sterntours-mail.loadbalancer.server.port=8025" - - "traefik.docker.network=proxy" -networks: - sail: - driver: bridge - proxy: - external: true -volumes: - sail-mysql: - driver: local - sail-mysql-stern: - driver: local - sail-redis: - driver: local diff --git a/mein.sterntours.de.code-workspace b/mein.sterntours.de.code-workspace deleted file mode 100644 index 7f1e073..0000000 --- a/mein.sterntours.de.code-workspace +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "STERN TOURS [DEV CONTAINER]", - "folders": [ - { - "path": "." - } - ] -} \ No newline at end of file diff --git a/packages/digital-bird/shoppingcart/LICENSE b/packages/digital-bird/shoppingcart/LICENSE new file mode 100644 index 0000000..61c6afc --- /dev/null +++ b/packages/digital-bird/shoppingcart/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Rob Gloudemans + +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. \ No newline at end of file diff --git a/packages/digital-bird/shoppingcart/README.md b/packages/digital-bird/shoppingcart/README.md new file mode 100644 index 0000000..c03bb4c --- /dev/null +++ b/packages/digital-bird/shoppingcart/README.md @@ -0,0 +1,491 @@ +## LaravelShoppingcart +[![License](https://poser.pugx.org/gloudemans/shoppingcart/license)](https://packagist.org/packages/gloudemans/shoppingcart) + +A simple shoppingcart implementation for Laravel. + +## Installation + +Install the package through [Composer](http://getcomposer.org/). + +Run the Composer require command from the Terminal: + + composer require digital-bird/shoppingcart + +If you're using Laravel 5.5, this is all there is to do. + +Should you still be on version 5.4 of Laravel, the final steps for you are to add the service provider of the package and alias the package. To do this open your `config/app.php` file. + +Add a new line to the `providers` array: + + Gloudemans\Shoppingcart\ShoppingcartServiceProvider::class + +And optionally add a new line to the `aliases` array: + + 'Cart' => Gloudemans\Shoppingcart\Facades\Cart::class, + +Now you're ready to start using the shoppingcart in your application. + +**As of version 2 of this package it's possibly to use dependency injection to inject an instance of the Cart class into your controller or other class** + +## Overview +Look at one of the following topics to learn more about LaravelShoppingcart + +* [Usage](#usage) +* [Collections](#collections) +* [Instances](#instances) +* [Models](#models) +* [Database](#database) +* [Exceptions](#exceptions) +* [Events](#events) +* [Example](#example) + +## Usage + +The shoppingcart gives you the following methods to use: + +### Cart::add() + +Adding an item to the cart is really simple, you just use the `add()` method, which accepts a variety of parameters. + +In its most basic form you can specify the id, name, quantity, price, and tax rate of the product you'd like to add to the cart. + +```php +Cart::add('293ad', 'Product 1', 1, 9.99, 10); +``` + +As an optional sixth parameter you can pass it options, so you can add multiple items with the same id, but with (for instance) a different size. + +The fifth parameter is the tax rate. Eg 10 for 10%. + +```php +Cart::add('293ad', 'Product 1', 1, 9.99, 10, ['size' => 'large']); +``` + +**The `add()` method will return an CartItem instance of the item you just added to the cart.** + +Maybe you prefer to add the item using an array? As long as the array contains the required keys, you can pass it to the method. The options key is optional. + +```php +Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'taxRate' => 10, 'options' => ['size' => 'large']]); +``` + +New in version 2 of the package is the possibility to work with the [Buyable](#buyable) interface. The way this works is that you have a model implement the `Buyable` interface, which will make you implement a few methods so the package knows how to get the id, name and price from your model. +This way you can just pass the `add()` method a model and the quantity and it will automatically add it to the cart. + +The path to the `Buyable` interface is: + + Gloudemans\Shoppingcart\Contracts\Buyable; + +**As an added bonus it will automatically associate the model with the CartItem** + +```php +Cart::add($product, 1, ['size' => 'large']); +``` +As an optional third parameter you can add options. +```php +Cart::add($product, 1, ['size' => 'large']); +``` + +Finally, you can also add multipe items to the cart at once. +You can just pass the `add()` method an array of arrays, or an array of Buyables and they will be added to the cart. + +**When adding multiple items to the cart, the `add()` method will return an array of CartItems.** + +```php +Cart::add([ + ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'taxRate' => 10], + ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'taxRate' => 10, 'options' => ['size' => 'large']] +]); + +Cart::add([$product1, $product2]); + +``` + +### Cart::update() + +To update an item in the cart, you'll first need the rowId of the item. +Next you can use the `update()` method to update it. + +If you simply want to update the quantity, you'll pass the update method the rowId and the new quantity: + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::update($rowId, 2); // Will update the quantity +``` + +If you want to update more attributes of the item, you can either pass the update method an array or a `Buyable` as the second parameter. This way you can update all information of the item with the given rowId. + +```php +Cart::update($rowId, ['name' => 'Product 1']); // Will update the name + +Cart::update($rowId, $product); // Will update the id, name and price + +``` + +### Cart::remove() + +To remove an item for the cart, you'll again need the rowId. This rowId you simply pass to the `remove()` method and it will remove the item from the cart. + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::remove($rowId); +``` + +### Cart::get() + +If you want to get an item from the cart using its rowId, you can simply call the `get()` method on the cart and pass it the rowId. + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::get($rowId); +``` + +### Cart::content() + +Of course you also want to get the carts content. This is where you'll use the `content` method. This method will return a Collection of CartItems which you can iterate over and show the content to your customers. + +```php +Cart::content(); +``` + +This method will return the content of the current cart instance, if you want the content of another instance, simply chain the calls. + +```php +Cart::instance('wishlist')->content(); +``` + +### Cart::destroy() + +If you want to completely remove the content of a cart, you can call the destroy method on the cart. This will remove all CartItems from the cart for the current cart instance. + +```php +Cart::destroy(); +``` + +### Cart::total() + +The `total()` method can be used to get the calculated total of all items in the cart, given there price and quantity. + +```php +Cart::total(); +``` + +The method will automatically format the result, which you can tweak using the three optional parameters + +```php +Cart::total($decimals, $decimalSeperator, $thousandSeperator); +``` + +You can set the default number format in the config file. + +**If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the total property `$cart->total`** + +### Cart::tax() + +The `tax()` method can be used to get the calculated amount of tax for all items in the cart, given there price and quantity. + +```php +Cart::tax(); +``` + +The method will automatically format the result, which you can tweak using the three optional parameters + +```php +Cart::tax($decimals, $decimalSeperator, $thousandSeperator); +``` + +You can set the default number format in the config file. + +**If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the tax property `$cart->tax`** + +### Cart::subtotal() + +The `subtotal()` method can be used to get the total of all items in the cart, minus the total amount of tax. + +```php +Cart::subtotal(); +``` + +The method will automatically format the result, which you can tweak using the three optional parameters + +```php +Cart::subtotal($decimals, $decimalSeperator, $thousandSeperator); +``` + +You can set the default number format in the config file. + +**If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the subtotal property `$cart->subtotal`** + +### Cart::count() + +If you want to know how many items there are in your cart, you can use the `count()` method. This method will return the total number of items in the cart. So if you've added 2 books and 1 shirt, it will return 3 items. + +```php +Cart::count(); +``` + +### Cart::search() + +To find an item in the cart, you can use the `search()` method. + +**This method was changed on version 2** + +Behind the scenes, the method simply uses the filter method of the Laravel Collection class. This means you must pass it a Closure in which you'll specify you search terms. + +If you for instance want to find all items with an id of 1: + +```php +$cart->search(function ($cartItem, $rowId) { + return $cartItem->id === 1; +}); +``` + +As you can see the Closure will receive two parameters. The first is the CartItem to perform the check against. The second parameter is the rowId of this CartItem. + +**The method will return a Collection containing all CartItems that where found** + +This way of searching gives you total control over the search process and gives you the ability to create very precise and specific searches. + +## Collections + +On multiple instances the Cart will return to you a Collection. This is just a simple Laravel Collection, so all methods you can call on a Laravel Collection are also available on the result. + +As an example, you can quicky get the number of unique products in a cart: + +```php +Cart::content()->count(); +``` + +Or you can group the content by the id of the products: + +```php +Cart::content()->groupBy('id'); +``` + +## Instances + +The packages supports multiple instances of the cart. The way this works is like this: + +You can set the current instance of the cart by calling `Cart::instance('newInstance')`. From this moment, the active instance of the cart will be `newInstance`, so when you add, remove or get the content of the cart, you're work with the `newInstance` instance of the cart. +If you want to switch instances, you just call `Cart::instance('otherInstance')` again, and you're working with the `otherInstance` again. + +So a little example: + +```php +Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99); + +// Get the content of the 'shopping' cart +Cart::content(); + +Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, ['size' => 'medium']); + +// Get the content of the 'wishlist' cart +Cart::content(); + +// If you want to get the content of the 'shopping' cart again +Cart::instance('shopping')->content(); + +// And the count of the 'wishlist' cart again +Cart::instance('wishlist')->count(); +``` + +**N.B. Keep in mind that the cart stays in the last set instance for as long as you don't set a different one during script execution.** + +**N.B.2 The default cart instance is called `default`, so when you're not using instances,`Cart::content();` is the same as `Cart::instance('default')->content()`.** + +## Models + +Because it can be very convenient to be able to directly access a model from a CartItem is it possible to associate a model with the items in the cart. Let's say you have a `Product` model in your application. With the `associate()` method, you can tell the cart that an item in the cart, is associated to the `Product` model. + +That way you can access your model right from the `CartItem`! + +The model can be accessed via the `model` property on the CartItem. + +**If your model implements the `Buyable` interface and you used your model to add the item to the cart, it will associate automatically.** + +Here is an example: + +```php + +// First we'll add the item to the cart. +$cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, ['size' => 'large']); + +// Next we associate a model with the item. +Cart::associate($cartItem->rowId, 'Product'); + +// Or even easier, call the associate method on the CartItem! +$cartItem->associate('Product'); + +// You can even make it a one-liner +Cart::add('293ad', 'Product 1', 1, 9.99, ['size' => 'large'])->associate('Product'); + +// Now, when iterating over the content of the cart, you can access the model. +foreach(Cart::content() as $row) { + echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.'; +} +``` + +### Buyable + +For the convenience of faster adding items to cart and their automatic association, your model can implement `Buyable` interface. To do so, it must implement such functions: + +```php + public function getBuyableIdentifier(){ + return $this->id; + } + + public function getBuyableDescription(){ + return $this->name; + } + + public function getBuyablePrice(){ + return $this->price; + } +``` + +Example: + +```php +id; + } + + public function getBuyableDescription($options = null) { + return $this->name; + } + + public function getBuyablePrice($options = null) { + return $this->price; + } +} +``` + + +## Database + +- [Config](#configuration) +- [Storing the cart](#storing-the-cart) +- [Restoring the cart](#restoring-the-cart) + +### Configuration +To save cart into the database so you can retrieve it later, the package needs to know which database connection to use and what the name of the table is. +By default the package will use the default database connection and use a table named `shoppingcart`. +If you want to change these options, you'll have to publish the `config` file. + + php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config" + +This will give you a `cart.php` config file in which you can make the changes. + +To make your life easy, the package also includes a ready to use `migration` which you can publish by running: + + php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations" + +This will place a `shoppingcart` table's migration file into `database/migrations` directory. Now all you have to do is run `php artisan migrate` to migrate your database. + +### Storing the cart +To store your cart instance into the database, you have to call the `store($identifier) ` method. Where `$identifier` is a random key, for instance the id or username of the user. + + Cart::store('username'); + + // To store a cart instance named 'wishlist' + Cart::instance('wishlist')->store('username'); + +### Restoring the cart +If you want to retrieve the cart from the database and restore it, all you have to do is call the `restore($identifier)` where `$identifier` is the key you specified for the `store` method. + + Cart::restore('username'); + + // To restore a cart instance named 'wishlist' + Cart::instance('wishlist')->restore('username'); + +## Exceptions + +The Cart package will throw exceptions if something goes wrong. This way it's easier to debug your code using the Cart package or to handle the error based on the type of exceptions. The Cart packages can throw the following exceptions: + +| Exception | Reason | +| ---------------------------- | ---------------------------------------------------------------------------------- | +| *CartAlreadyStoredException* | When trying to store a cart that was already stored using the specified identifier | +| *InvalidRowIDException* | When the rowId that got passed doesn't exists in the current cart instance | +| *UnknownModelException* | When you try to associate an none existing model to a CartItem. | + +## Events + +The cart also has events build in. There are five events available for you to listen for. + +| Event | Fired | Parameter | +| ------------- | ---------------------------------------- | -------------------------------- | +| cart.added | When an item was added to the cart. | The `CartItem` that was added. | +| cart.updated | When an item in the cart was updated. | The `CartItem` that was updated. | +| cart.removed | When an item is removed from the cart. | The `CartItem` that was removed. | +| cart.stored | When the content of a cart was stored. | - | +| cart.restored | When the content of a cart was restored. | - | + +## Example + +Below is a little example of how to list the cart content in a table: + +```php + +// Add some items in your Controller. +Cart::add('192ao12', 'Product 1', 1, 9.99); +Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']); + +// Display the content in a View. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProductQtyPriceSubtotal
+

name; ?>

+

options->has('size') ? $row->options->size : ''); ?>

+
$price; ?>$total; ?>
 Subtotal
 Tax
 Total
+``` diff --git a/packages/digital-bird/shoppingcart/composer.json b/packages/digital-bird/shoppingcart/composer.json new file mode 100644 index 0000000..7e7d49b --- /dev/null +++ b/packages/digital-bird/shoppingcart/composer.json @@ -0,0 +1,50 @@ +{ + "name": "digital-bird/shoppingcart", + "version": "3.1.0", + "description": "Laravel Shoppingcart", + "keywords": [ + "laravel", + "shoppingcart" + ], + "license": "MIT", + "authors": [ + { + "name": "Rob Gloudemans", + "email": "info@robgloudemans.nl" + } + ], + "require": { + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/session": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/events": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" + }, + "require-dev": { + "phpunit/phpunit": "~5.0|~6.0|~7.0|~8.0|~9,0", + "mockery/mockery": "~0.9.0", + "orchestra/testbench": "~3.1" + }, + "autoload": { + "psr-4": { + "Gloudemans\\Shoppingcart\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Gloudemans\\Tests\\Shoppingcart\\": "tests/" + } + }, + "suggest": { + "gloudemans/notify": "Simple flash notifications for Laravel" + }, + "minimum-stability": "stable", + "extra": { + "laravel": { + "providers": [ + "Gloudemans\\Shoppingcart\\ShoppingcartServiceProvider" + ], + "aliases": { + "Cart": "Gloudemans\\Shoppingcart\\Facades\\Cart" + } + } + } +} diff --git a/packages/digital-bird/shoppingcart/config/cart.php b/packages/digital-bird/shoppingcart/config/cart.php new file mode 100644 index 0000000..3546b0c --- /dev/null +++ b/packages/digital-bird/shoppingcart/config/cart.php @@ -0,0 +1,75 @@ + 10, + + /* + |-------------------------------------------------------------------------- + | Shoppingcart database settings + |-------------------------------------------------------------------------- + | + | Here you can set the connection that the shoppingcart should use when + | storing and restoring a cart. + | + */ + + 'database' => [ + + 'connection' => null, + + 'table' => 'shoppingcart', + + ], + + /* + |-------------------------------------------------------------------------- + | Destroy the cart on user logout + |-------------------------------------------------------------------------- + | + | When this option is set to 'true' the cart will automatically + | destroy all cart instances when the user logs out. + | + */ + + 'destroy_on_logout' => false, + + /* + |-------------------------------------------------------------------------- + | Default number format + |-------------------------------------------------------------------------- + | + | This defaults will be used for the formated numbers if you don't + | set them in the method call. + | + */ + + 'format' => [ + + 'decimals' => 2, + + 'decimal_point' => '.', + + 'thousand_seperator' => '' + + ], + + /* + |-------------------------------------------------------------------------- + | Allows you to choose if the discounts applied to fees + |-------------------------------------------------------------------------- + | + */ + 'discountOnFees' => false, + +]; diff --git a/packages/digital-bird/shoppingcart/database/migrations/0000_00_00_000000_create_shoppingcart_table.php b/packages/digital-bird/shoppingcart/database/migrations/0000_00_00_000000_create_shoppingcart_table.php new file mode 100644 index 0000000..fc5bb26 --- /dev/null +++ b/packages/digital-bird/shoppingcart/database/migrations/0000_00_00_000000_create_shoppingcart_table.php @@ -0,0 +1,30 @@ +string('identifier'); + $table->string('instance'); + $table->longText('content'); + $table->nullableTimestamps(); + + $table->primary(['identifier', 'instance']); + }); + } + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop(config('cart.database.table')); + } +} diff --git a/packages/digital-bird/shoppingcart/phpunit.xml b/packages/digital-bird/shoppingcart/phpunit.xml new file mode 100644 index 0000000..655d24e --- /dev/null +++ b/packages/digital-bird/shoppingcart/phpunit.xml @@ -0,0 +1,16 @@ + + + + + ./tests/ + + + \ No newline at end of file diff --git a/packages/digital-bird/shoppingcart/src/CanBeBought.php b/packages/digital-bird/shoppingcart/src/CanBeBought.php new file mode 100644 index 0000000..e122d1f --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/CanBeBought.php @@ -0,0 +1,54 @@ +getKey() : + $this->id; + } + + /** + * Get the description or title of the Buyable item. + * + * @return string + */ + public function getBuyableDescription($options = null) + { + if (property_exists($this, 'name')) { + return $this->name; + } + + if (property_exists($this, 'title')) { + return $this->title; + } + + if (property_exists($this, 'description')) { + return $this->description; + } + + return null; + } + + /** + * Get the price of the Buyable item. + * + * @return float + */ + public function getBuyablePrice($options = null) + { + if (property_exists($this, 'price')) { + return $this->price; + } + + return null; + } +} diff --git a/packages/digital-bird/shoppingcart/src/Cart.php b/packages/digital-bird/shoppingcart/src/Cart.php new file mode 100644 index 0000000..0f7e3e3 --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/Cart.php @@ -0,0 +1,799 @@ +items = new Collection; + $this->fees = new Collection; + $this->session = $session; + $this->events = $events; + + $this->instance(self::DEFAULT_INSTANCE); + } + + /** + * Set the current cart instance. + * + * @param string|null $instance + * @return \Gloudemans\Shoppingcart\Cart + */ + public function instance($instance = null) + { + $instance = $instance ?: self::DEFAULT_INSTANCE; + + $this->instance = sprintf('%s.%s', 'cart', $instance); + + return $this; + } + + /** + * Get the current cart instance. + * + * @return string + */ + public function currentInstance() + { + return str_replace('cart.', '', $this->instance); + } + + /** + * Add an item to the cart. + * + * @param mixed $id + * @param mixed $name + * @param int|float $qty + * @param float $price + * @param array $options + * @return \Gloudemans\Shoppingcart\CartItem + */ + public function add($id, $name = null, $qty = null, $price = null, $taxRate = null, array $options = []) + { + if ($this->isMulti($id)) { + return array_map(function ($item) { + return $this->add($item); + }, $id); + } + + if ($id instanceof CartItem) { + $cartItem = $id; + } else { + $cartItem = $this->createCartItem($id, $name, $qty, $price, $taxRate, $options); + } + + $content = $this->getContent(); + + if ($content->has($cartItem->rowId)) { + $cartItem->qty += $content->get($cartItem->rowId)->qty; + } + + $content->put($cartItem->rowId, $cartItem); + + $this->items = $content; + + $this->session->put($this->instance, $this->toArray()); + + $this->events->dispatch('cart.added', [ + [ + 'cartInstance' => $this->currentInstance(), + 'cartItem' => $cartItem, + ] + ]); + + return $cartItem; + } + + /** + * Update the cart item with the given rowId. + * + * @param string $rowId + * @param mixed $qty + * @return \Gloudemans\Shoppingcart\CartItem + */ + public function update($rowId, $qty) + { + $cartItem = $this->get($rowId); + + if ($qty instanceof Buyable) { + $cartItem->updateFromBuyable($qty); + } elseif (is_array($qty)) { + $cartItem->updateFromArray($qty); + } else { + $cartItem->qty = $qty; + } + + $content = $this->getContent(); + + if ($rowId !== $cartItem->rowId) { + if ($content->has($cartItem->rowId)) { + $existingCartItem = $this->get($cartItem->rowId); + $cartItem->setQuantity($existingCartItem->qty + $cartItem->qty); + } + + $content = $content->mapWithKeys(function ($val, $key) use ($rowId, $cartItem) { + if ($key === $rowId) { + return [ $cartItem->rowId => $cartItem ]; + } + + return [ $key => $val ]; + }); + + $this->items = $content; + } + + if ($cartItem->qty <= 0) { + $this->remove($cartItem->rowId); + return; + } + + $this->session->put($this->instance, $this->toArray()); + + $this->events->dispatch('cart.updated', [ + [ + 'cartInstance' => $this->currentInstance(), + 'cartItem' => $cartItem, + ] + ]); + + return $cartItem; + } + + /** + * Remove the cart item with the given rowId from the cart. + * + * @param string $rowId + * @return void + */ + public function remove($rowId) + { + $cartItem = $this->get($rowId); + + $content = $this->getContent(); + + $content->pull($cartItem->rowId); + + $this->items = $content; + + $this->session->put($this->instance, $this->toArray()); + + $this->events->dispatch('cart.removed', [ + [ + 'cartInstance' => $this->currentInstance(), + 'cartItem' => $cartItem, + ] + ]); + } + + /** + * Get a cart item from the cart by its rowId. + * + * @param string $rowId + * @return \Gloudemans\Shoppingcart\CartItem + */ + public function get($rowId) + { + $content = $this->getContent(); + + if ($content->has($rowId) === false) { + throw new InvalidRowIDException("The cart does not contain rowId {$rowId}."); + } + + return $content->get($rowId); + } + + /** + * Destroy the current cart instance. + * + * @return void + */ + public function destroy() + { + $this->session->remove($this->instance); + } + + /** + * Get the content of the cart. + * + * @return \Illuminate\Support\Collection + */ + public function content() + { + return $this->getContent(); + } + + /** + * Get the number of items in the cart. + * + * @return int|float + */ + public function count() + { + $content = $this->getContent(); + + return $content->sum('qty'); + } + + /** + * Get the total price of the items in the cart. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function total($decimals = null, $decimalPoint = null, $thousandSeperator = null, $withFees = true) + { + $content = $this->getContent(); + + $total = $content->reduce(function ($total, CartItem $cartItem) { + return $total + ($cartItem->qty * $cartItem->priceTax); + }, 0); + + if ($withFees === true) { + $fees = $this->feeTotal(null, null, null, true); + + $total = $total + $fees; + } + + return $this->numberFormat($total, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Get the total tax of the items in the cart. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return float + */ + public function tax($decimals = null, $decimalPoint = null, $thousandSeperator = null, $withFees = true) + { + $content = $this->getContent(); + + $tax = $content->reduce(function ($tax, CartItem $cartItem) { + return $tax + ($cartItem->qty * $cartItem->tax); + }, 0); + + if ($withFees === true) { + $fees = $this->feeTax(); + + $tax = $tax + floatval($fees); + } + + return $this->numberFormat($tax, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Get the total tax of the items in the cart. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return float + */ + public function feeTax($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $content = $this->getContent(); + + $tax = 0; + + foreach ($this->getFees() as $fee) { + $tax += $fee->tax; + } + + return $this->numberFormat($tax, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Get the subtotal (total - tax) of the items in the cart. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return float + */ + public function subtotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $content = $this->getContent(); + + $subTotal = $content->reduce(function ($subTotal, CartItem $cartItem) { + return $subTotal + ($cartItem->qty * $cartItem->price); + }, 0); + + return $this->numberFormat($subTotal, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Get the subtotal (total - tax) of the items in the cart. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return float + */ + public function subtotalTax($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $content = $this->getContent(); + + $subTotal = $content->reduce(function ($subTotal, CartItem $cartItem) { + return $subTotal + ($cartItem->qty * $cartItem->priceTax); + }, 0); + + return $this->numberFormat($subTotal, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Search the cart content for a cart item matching the given search closure. + * + * @param Closure $search + * @return \Illuminate\Support\Collection + */ + public function search(Closure $search) + { + $content = $this->getContent(); + + return $content->filter($search); + } + + /** + * Associate the cart item with the given rowId with the given model. + * + * @param string $rowId + * @param mixed $model + * @return void + */ + public function associate($rowId, $model) + { + if ( + is_string($model) === true && + class_exists($model) === false + ) { + throw new UnknownModelException("The supplied model {$model} does not exist."); + } + + $cartItem = $this->get($rowId); + + $cartItem->associate($model); + + $content = $this->getContent(); + + $content->put($cartItem->rowId, $cartItem); + + $this->session->put($this->instance, $this->toArray()); + } + + /** + * Set the tax rate for the cart item with the given rowId. + * + * @param string $rowId + * @param int|float $taxRate + * @return void + */ + public function setTax($rowId, $taxRate) + { + $cartItem = $this->get($rowId); + + $cartItem->setTaxRate($taxRate); + + $content = $this->getContent(); + + $content->put($cartItem->rowId, $cartItem); + + $this->items = $content; + + $this->session->put($this->instance, $this->toArray()); + } + + /** + * Store an the current instance of the cart. + * + * @param mixed $identifier + * @return void + */ + public function store($identifier) + { + // Remove any existing identifiers + // Although possibly first or update could work in future + $this + ->getConnection() + ->table($this->getTableName()) + ->where('identifier', $identifier) + ->delete(); + + // Insert into the database with the new cart + $this + ->getConnection() + ->table($this->getTableName()) + ->insert([ + 'identifier' => $identifier, + 'instance' => $this->currentInstance(), + 'content' => serialize($this->toArray()), + 'created_at'=> new DateTime(), + ]); + + $this->events->dispatch('cart.stored', $this->currentInstance()); + } + + /** + * Restore the cart with the given identifier. + * + * @param mixed $identifier + * @return void + */ + public function restore($identifier) + { + if ($this->storedCartWithIdentifierExists($identifier) === false) { + return; + } + + // Find any existing carts by identifier + $stored = $this + ->getConnection() + ->table($this->getTableName()) + ->where('identifier', $identifier) + ->first(); + + // Unserialize the content (either array if new, or collection if old) + $storedContent = unserialize($stored->content); + + $currentInstance = $this->currentInstance(); + + $this->instance($stored->instance); + + $content = $this->getContent(); + + // If the new approach and is array, set this class up. + // Note that it overrides any existing items in cart + // Does not add to existing. + if (is_array($storedContent)) { + $this->fromArray($storedContent); + } + + // If the old approach and is Collection, push into existing items + if ($storedContent instanceof Collection) { + foreach ($storedContent as $cartItem) { + $content->put($cartItem->rowId, $cartItem); + } + } + + $this->events->dispatch('cart.restored', $this->currentInstance()); + + $this->session->put($this->instance, $this->toArray()); + + $this->instance($currentInstance); + } + + /** + * Gets a specific fee from the fees array. + * + * @param $name + * + * @return mixed + */ + public function getFee($name) + { + return $this->fees->get($name, new CartFee(null, null)); + } + + /** + * Allows to charge for additional fees that may or may not be taxable + * ex - service fee , delivery fee, tips. + * + * Because it uses ->put, the name must be unique otherwise will be overwritten. + * + * @param $name + * @param $amount + * @param $taxRate + * @param array $options + */ + public function addFee($name, $amount, $taxRate = null, array $options = []) + { + $this->fees->put($name, new CartFee($amount, $taxRate, $options)); + + $this->session->put($this->instance, $this->toArray()); + } + + /** + * Removes a fee from the fee array. + * + * @todo test to see if i need to restore this + * + * @param $name + */ + public function removeFee($name) + { + $this->fees->forget($name); + + $this->session->put($this->instance, $this->toArray()); + } + + /** + * Removes all the fees set in the cart. + */ + public function removeFees() + { + $this->fees = new Collection; + + $this->session->put($this->instance, $this->toArray()); + } + + /** + * Gets all the fee totals. + * + * @param bool $format + * @param bool $withTax + * + * @return string + */ + public function feeTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null, $withTax = true) + { + $feeTotal = 0; + + foreach ($this->getFees() as $fee) { + $feeTotal += $fee->amount; + + if ($withTax === true && $fee->taxRate > 0) { + $feeTotal += $fee->tax; + } + } + + return $this->numberFormat($feeTotal, null, null, null); + } + + /** + * Gets all the fees on the cart object. + * + * @return mixed + */ + public function getFees() + { + return $this->fees; + } + + /** + * Magic method to make accessing the total, tax and subtotal properties possible. + * + * @param string $attribute + * @return float|null + */ + public function __get($attribute) + { + if ($attribute === 'total') { + return $this->total(); + } + + if ($attribute === 'feeTotal') { + return $this->feeTotal(null, null, null, false); + } + + if ($attribute === 'feeTotalTax') { + return $this->feeTotal(null, null, null, true); + } + + if ($attribute === 'tax') { + return $this->tax(); + } + + if ($attribute === 'feeTax') { + return $this->feeTax(); + } + + if ($attribute === 'subtotal') { + return $this->subtotal(); + } + + if ($attribute === 'subtotalTax') { + return $this->subtotalTax(); + } + + return null; + } + + /** + * @return array + */ + public function toArray() + { + return [ + 'items' => $this->items, + 'fees' => $this->fees, + ]; + } + + /** + * @param $array + * @return $this + */ + public function fromArray($array) + { + $this->items = $array['items']; + $this->fees = $array['fees']; + + return $this; + } + + /** + * Deletes the stored cart with given identifier + * + * @param mixed $identifier + */ + protected function deleteStoredCart($identifier) + { + $this + ->getConnection() + ->table($this->getTableName()) + ->where('identifier', $identifier) + ->delete(); + } + + /** + * Get the carts content, if there is no cart content set yet, return a new empty Collection + * + * @return \Illuminate\Support\Collection + */ + protected function getContent(): Collection + { + $instanceExists = $this->session->has($this->instance); + + if ($instanceExists === false) { + $this->items = new Collection; + + return $this->items; + } + + $instance = $this->session->get($this->instance); + + // If new approach, set $this variables + if (is_array($instance) === true) { + $this->items = $instance['items']; + $this->fees = $instance['fees']; + } + + if ($instance instanceof Collection) { + $this->items = $instance; + } + + return $this->items; + } + + /** + * Create a new CartItem from the supplied attributes. + * + * @param mixed $id + * @param mixed $name + * @param int|float $qty + * @param float $price + * @param array $options + * @return \Gloudemans\Shoppingcart\CartItem + */ + private function createCartItem($id, $name, $qty, $price, $taxRate, array $options): CartItem + { + if ($id instanceof Buyable) { + $cartItem = CartItem::fromBuyable($id, $qty ?: []); + $cartItem->setQuantity($name ?: 1); + $cartItem->associate($id); + } elseif (is_array($id)) { + $cartItem = CartItem::fromArray($id); + $cartItem->setQuantity($id['qty']); + } else { + $cartItem = CartItem::fromAttributes($id, $name, $price, $options); + $cartItem->setQuantity($qty); + } + + $taxRate = is_int($taxRate) ? $taxRate : config('cart.tax'); + + $cartItem->setTaxRate($taxRate); + + return $cartItem; + } + + /** + * Check if the item is a multidimensional array or an array of Buyables. + * + * @param mixed $item + * @return bool + */ + private function isMulti($item): bool + { + if (is_array($item) === false) { + return false; + } + + return is_array(head($item)) || head($item) instanceof Buyable === true; + } + + /** + * @param $identifier + * @return bool + */ + protected function storedCartWithIdentifierExists($identifier): bool + { + return $this + ->getConnection() + ->table($this->getTableName()) + ->where('identifier', $identifier) + ->exists(); + } + + /** + * Get the database connection. + * + * @return \Illuminate\Database\Connection + */ + protected function getConnection() + { + $connectionName = $this->getConnectionName(); + + return app(DatabaseManager::class)->connection($connectionName); + } + + /** + * Get the database table name. + * + * @return string + */ + protected function getTableName() + { + return config('cart.database.table', 'shoppingcart'); + } + + /** + * Get the database connection name. + * + * @return string + */ + private function getConnectionName() + { + $connection = config('cart.database.connection'); + + return is_null($connection) ? config('database.default') : $connection; + } +} diff --git a/packages/digital-bird/shoppingcart/src/CartFee.php b/packages/digital-bird/shoppingcart/src/CartFee.php new file mode 100644 index 0000000..0aff34c --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/CartFee.php @@ -0,0 +1,75 @@ +amount = floatval($amount); + $this->taxRate = is_null($taxRate) ? config('cart.tax') : $taxRate; + $this->options = new CartFeeOptions($options); + } + + /** + * Gets the formatted amount. + * + * @param bool $format + * @param bool $withTax + * + * @return string + */ + public function getAmount($format = true, $withTax = false) + { + $total = $this->amount; + + if ($withTax) { + $total += $this->taxRate * $total; + } + + return $this->numberFormat($total); + } + + /** + * @return string + */ + public function tax() + { + $tax = $this->amount * $this->taxRate / 100; + + return $this->numberFormat($tax); + } + + /** + * Magic method to make accessing the total, tax and subtotal properties possible. + * + * @param string $attribute + * @return float|null + */ + public function __get($attribute) + { + if ($attribute === 'tax') { + return $this->tax(); + } + + return null; + } +} diff --git a/packages/digital-bird/shoppingcart/src/CartFeeOptions.php b/packages/digital-bird/shoppingcart/src/CartFeeOptions.php new file mode 100644 index 0000000..dd91b92 --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/CartFeeOptions.php @@ -0,0 +1,19 @@ +get($key); + } +} diff --git a/packages/digital-bird/shoppingcart/src/CartItem.php b/packages/digital-bird/shoppingcart/src/CartItem.php new file mode 100644 index 0000000..bb5e1a8 --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/CartItem.php @@ -0,0 +1,422 @@ +id = $id; + $this->name = $name; + $this->price = floatval($price); + $this->options = new CartItemOptions($options); + $this->rowId = $this->generateRowId($id, $options); + } + + /** + * Returns the formatted price without TAX. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function price($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + return $this->numberFormat($this->price, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Returns the formatted price with TAX. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function priceTax($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $priceTax = number_format(($this->price + $this->tax), $decimals, '.', ''); + + return $this->numberFormat($priceTax, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Returns the formatted subtotal. + * Subtotal is price for whole CartItem without TAX + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function subtotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $subtotal = number_format(($this->qty * $this->price), $decimals, '.', ''); + + return $this->numberFormat($subtotal, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Returns the formatted subtotal. + * Subtotal is price for whole CartItem with TAX + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function subtotalTax($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $subtotal = number_format(($this->qty * $this->priceTax), $decimals, '.', ''); + + return $this->numberFormat($subtotal, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Returns the formatted total. + * Total is price for whole CartItem with tax + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function total($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $total = number_format(($this->qty * priceTax), $decimals, '.', ''); + + return $this->numberFormat($total, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Returns the formatted tax. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function tax($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $tax = number_format(($this->price * ($this->taxRate / 100)), $decimals, '.', ''); + + return $this->numberFormat($tax, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Returns the formatted tax. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * @return string + */ + public function taxTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + $taxTotal = number_format(($this->tax * $this->qty), $decimals, '.', ''); + + return $this->numberFormat($taxTotal, $decimals, $decimalPoint, $thousandSeperator); + } + + /** + * Set the quantity for this cart item. + * + * @param int|float $qty + */ + public function setQuantity($qty) + { + if(empty($qty) || ! is_numeric($qty)) + throw new \InvalidArgumentException('Please supply a valid quantity.'); + + $this->qty = $qty; + } + + /** + * Update the cart item from a Buyable. + * + * @param \Gloudemans\Shoppingcart\Contracts\Buyable $item + * @return void + */ + public function updateFromBuyable(Buyable $item) + { + $this->id = $item->getBuyableIdentifier($this->options); + $this->name = $item->getBuyableDescription($this->options); + $this->price = $item->getBuyablePrice($this->options); + $this->priceTax = $this->price + $this->tax; + } + + /** + * Update the cart item from an array. + * + * @param array $attributes + * @return void + */ + public function updateFromArray(array $attributes) + { + $this->id = array_get($attributes, 'id', $this->id); + $this->qty = array_get($attributes, 'qty', $this->qty); + $this->name = array_get($attributes, 'name', $this->name); + $this->price = array_get($attributes, 'price', $this->price); + $this->priceTax = $this->price + $this->tax; + $this->options = new CartItemOptions(array_get($attributes, 'options', $this->options)); + + $this->rowId = $this->generateRowId($this->id, $this->options->all()); + } + + /** + * Associate the cart item with the given model. + * + * @param mixed $model + * @return \Gloudemans\Shoppingcart\CartItem + */ + public function associate($model) + { + $this->associatedModel = is_string($model) ? $model : get_class($model); + + return $this; + } + + /** + * Set the tax rate. + * + * @param int|float $taxRate + * @return \Gloudemans\Shoppingcart\CartItem + */ + public function setTaxRate($taxRate) + { + $this->taxRate = $taxRate; + + return $this; + } + + /** + * Set saved state. + * + * @param bool $bool + * @return \Gloudemans\Shoppingcart\CartItem + */ + public function setSaved($bool) + { + $this->isSaved = $bool; + + return $this; + } + + /** + * Get an attribute from the cart item or get the associated model. + * + * @param string $attribute + * @return mixed + */ + public function __get($attribute) + { + if(property_exists($this, $attribute)) { + return $this->{$attribute}; + } + + $decimals = config('cart.format.decimals') ?? 2; + + if ($attribute === 'priceTax') { + return $this->priceTax($decimals); + } + + if ($attribute === 'subtotal') { + return $this->subtotal($decimals); + } + + if ($attribute === 'subtotalTax') { + return $this->subtotalTax($decimals); + } + + if ($attribute === 'total') { + return $this->total($decimals); + } + + if ($attribute === 'tax') { + return $this->tax($decimals); + } + + if ($attribute === 'taxTotal') { + return $this->taxTotal($decimals); + } + + if($attribute === 'model' && isset($this->associatedModel)) { + return with(new $this->associatedModel)->find($this->id); + } + + return null; + } + + /** + * Create a new instance from a Buyable. + * + * @param \Gloudemans\Shoppingcart\Contracts\Buyable $item + * @param array $options + * @return \Gloudemans\Shoppingcart\CartItem + */ + public static function fromBuyable(Buyable $item, array $options = []) + { + return new self($item->getBuyableIdentifier($options), $item->getBuyableDescription($options), $item->getBuyablePrice($options), $options); + } + + /** + * Create a new instance from the given array. + * + * @param array $attributes + * @return \Gloudemans\Shoppingcart\CartItem + */ + public static function fromArray(array $attributes) + { + $options = array_get($attributes, 'options', []); + + return new self($attributes['id'], $attributes['name'], $attributes['price'], $options); + } + + /** + * Create a new instance from the given attributes. + * + * @param int|string $id + * @param string $name + * @param float $price + * @param array $options + * @return \Gloudemans\Shoppingcart\CartItem + */ + public static function fromAttributes($id, $name, $price, array $options = []) + { + return new self($id, $name, $price, $options); + } + + /** + * Generate a unique id for the cart item. + * + * @param string $id + * @param array $options + * @return string + */ + protected function generateRowId($id, array $options) + { + ksort($options); + + return md5($id . serialize($options)); + } + + /** + * Get the instance as an array. + * + * @return array + */ + public function toArray() + { + return [ + 'rowId' => $this->rowId, + 'id' => $this->id, + 'name' => $this->name, + 'qty' => $this->qty, + 'price' => $this->price, + 'options' => $this->options->toArray(), + 'tax' => $this->tax, + 'isSaved' => $this->isSaved, + 'subtotal' => $this->subtotal + ]; + } + + /** + * Convert the object to its JSON representation. + * + * @param int $options + * @return string + */ + public function toJson($options = 0) + { + return json_encode($this->toArray(), $options); + } +} diff --git a/packages/digital-bird/shoppingcart/src/CartItemOptions.php b/packages/digital-bird/shoppingcart/src/CartItemOptions.php new file mode 100644 index 0000000..1b80205 --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/CartItemOptions.php @@ -0,0 +1,19 @@ +get($key); + } +} diff --git a/packages/digital-bird/shoppingcart/src/Contracts/Buyable.php b/packages/digital-bird/shoppingcart/src/Contracts/Buyable.php new file mode 100644 index 0000000..f5bfeb7 --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/Contracts/Buyable.php @@ -0,0 +1,27 @@ +app->bind('cart', 'Gloudemans\Shoppingcart\Cart'); + + $config = __DIR__ . '/../config/cart.php'; + $this->mergeConfigFrom($config, 'cart'); + + $this->publishes([__DIR__ . '/../config/cart.php' => config_path('cart.php')], 'config'); + + $this->app['events']->listen(Logout::class, function () { + if ($this->app['config']->get('cart.destroy_on_logout')) { + $this->app->make(SessionManager::class)->forget('cart'); + } + }); + + if ( ! class_exists('CreateShoppingcartTable')) { + // Publish the migration + $timestamp = date('Y_m_d_His', time()); + + $this->publishes([ + __DIR__.'/../database/migrations/0000_00_00_000000_create_shoppingcart_table.php' => database_path('migrations/'.$timestamp.'_create_shoppingcart_table.php'), + ], 'migrations'); + } + } +} diff --git a/packages/digital-bird/shoppingcart/src/Traits/CartHelper.php b/packages/digital-bird/shoppingcart/src/Traits/CartHelper.php new file mode 100644 index 0000000..cb7968c --- /dev/null +++ b/packages/digital-bird/shoppingcart/src/Traits/CartHelper.php @@ -0,0 +1,35 @@ +count(); + + PHPUnit::assertEquals($items, $cart->count(), "Expected the cart to contain {$items} items, but got {$actual}."); + } + + /** + * Assert that the cart contains the given number of rows. + * + * @param int $rows + * @param \Gloudemans\Shoppingcart\Cart $cart + */ + public function assertRowsInCart($rows, Cart $cart) + { + $actual = $cart->content()->count(); + + PHPUnit::assertCount($rows, $cart->content(), "Expected the cart to contain {$rows} rows, but got {$actual}."); + } +} diff --git a/packages/digital-bird/shoppingcart/tests/CartItemTest.php b/packages/digital-bird/shoppingcart/tests/CartItemTest.php new file mode 100644 index 0000000..2a01714 --- /dev/null +++ b/packages/digital-bird/shoppingcart/tests/CartItemTest.php @@ -0,0 +1,56 @@ + 'XL', 'color' => 'red']); + $cartItem->setQuantity(2); + + $this->assertEquals([ + 'id' => 1, + 'name' => 'Some item', + 'price' => 10.00, + 'rowId' => '07d5da5550494c62daf9993cf954303f', + 'qty' => 2, + 'options' => [ + 'size' => 'XL', + 'color' => 'red' + ], + 'tax' => 0.0, + 'subtotal' => 20.00, + 'isSaved' => false + ], $cartItem->toArray()); + } + + /** @test */ + public function it_can_be_cast_to_json() + { + $cartItem = new CartItem(1, 'Some item', 10.00, ['size' => 'XL', 'color' => 'red']); + $cartItem->setQuantity(2); + + $this->assertJson($cartItem->toJson()); + + $json = '{"rowId":"07d5da5550494c62daf9993cf954303f","id":1,"name":"Some item","qty":2,"price":10,"options":{"size":"XL","color":"red"},"tax":"0.00","isSaved":false,"subtotal":"20.00"}'; + + $this->assertEquals($json, $cartItem->toJson()); + } +} diff --git a/packages/digital-bird/shoppingcart/tests/CartTest.php b/packages/digital-bird/shoppingcart/tests/CartTest.php new file mode 100644 index 0000000..4df29f0 --- /dev/null +++ b/packages/digital-bird/shoppingcart/tests/CartTest.php @@ -0,0 +1,945 @@ +set('cart.database.connection', 'testing'); + + $app['config']->set('session.driver', 'array'); + + $app['config']->set('database.default', 'testing'); + $app['config']->set('database.connections.testing', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + } + + /** + * Setup the test environment. + * + * @return void + */ + protected function setUp() + { + parent::setUp(); + + $this->app->afterResolving('migrator', function ($migrator) { + $migrator->path(realpath(__DIR__.'/../database/migrations')); + }); + } + + /** @test */ + public function it_has_a_default_instance() + { + $cart = $this->getCart(); + + $this->assertEquals(Cart::DEFAULT_INSTANCE, $cart->currentInstance()); + } + + /** @test */ + public function it_can_have_multiple_instances() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'First item')); + + $cart->instance('wishlist')->add(new BuyableProduct(2, 'Second item')); + + $this->assertItemsInCart(1, $cart->instance(Cart::DEFAULT_INSTANCE)); + $this->assertItemsInCart(1, $cart->instance('wishlist')); + } + + /** @test */ + public function it_can_add_an_item() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $this->assertEquals(1, $cart->count()); + + Event::assertDispatched('cart.added'); + } + + /** @test */ + public function it_will_return_the_cartitem_of_the_added_item() + { + Event::fake(); + + $cart = $this->getCart(); + + $cartItem = $cart->add(new BuyableProduct); + + $this->assertInstanceOf(CartItem::class, $cartItem); + $this->assertEquals('027c91341fd5cf4d2579b49c4b6a90da', $cartItem->rowId); + + Event::assertDispatched('cart.added'); + } + + /** @test */ + public function it_can_add_multiple_buyable_items_at_once() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add([new BuyableProduct(1), new BuyableProduct(2)]); + + $this->assertEquals(2, $cart->count()); + + Event::assertDispatched('cart.added'); + } + + /** @test */ + public function it_will_return_an_array_of_cartitems_when_you_add_multiple_items_at_once() + { + Event::fake(); + + $cart = $this->getCart(); + + $cartItems = $cart->add([new BuyableProduct(1), new BuyableProduct(2)]); + + $this->assertTrue(is_array($cartItems)); + $this->assertCount(2, $cartItems); + $this->assertContainsOnlyInstancesOf(CartItem::class, $cartItems); + + Event::assertDispatched('cart.added'); + } + + /** @test */ + public function it_can_add_an_item_from_attributes() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(1, 'Test item', 1, 10.00); + + $this->assertEquals(1, $cart->count()); + + Event::assertDispatched('cart.added'); + } + + /** @test */ + public function it_can_add_an_item_from_an_array() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(['id' => 1, 'name' => 'Test item', 'qty' => 1, 'price' => 10.00]); + + $this->assertEquals(1, $cart->count()); + + Event::assertDispatched('cart.added'); + } + + /** @test */ + public function it_can_add_multiple_array_items_at_once() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add([ + ['id' => 1, 'name' => 'Test item 1', 'qty' => 1, 'price' => 10.00], + ['id' => 2, 'name' => 'Test item 2', 'qty' => 1, 'price' => 10.00] + ]); + + $this->assertEquals(2, $cart->count()); + + Event::assertDispatched('cart.added'); + } + + /** @test */ + public function it_can_add_an_item_with_options() + { + Event::fake(); + + $cart = $this->getCart(); + + $options = ['size' => 'XL', 'color' => 'red']; + + $cart->add(new BuyableProduct, 1, $options); + + $cartItem = $cart->get('07d5da5550494c62daf9993cf954303f'); + + $this->assertInstanceOf(CartItem::class, $cartItem); + $this->assertEquals('XL', $cartItem->options->size); + $this->assertEquals('red', $cartItem->options->color); + + Event::assertDispatched('cart.added'); + } + + /** + * @test + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Please supply a valid identifier. + */ + public function it_will_validate_the_identifier() + { + $cart = $this->getCart(); + + $cart->add(null, 'Some title', 1, 10.00); + } + + /** + * @test + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Please supply a valid name. + */ + public function it_will_validate_the_name() + { + $cart = $this->getCart(); + + $cart->add(1, null, 1, 10.00); + } + + /** + * @test + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Please supply a valid quantity. + */ + public function it_will_validate_the_quantity() + { + $cart = $this->getCart(); + + $cart->add(1, 'Some title', 'invalid', 10.00); + } + + /** + * @test + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Please supply a valid price. + */ + public function it_will_validate_the_price() + { + $cart = $this->getCart(); + + $cart->add(1, 'Some title', 1, 'invalid'); + } + + /** @test */ + public function it_will_update_the_cart_if_the_item_already_exists_in_the_cart() + { + $cart = $this->getCart(); + + $item = new BuyableProduct; + + $cart->add($item); + $cart->add($item); + + $this->assertItemsInCart(2, $cart); + $this->assertRowsInCart(1, $cart); + } + + /** @test */ + public function it_will_keep_updating_the_quantity_when_an_item_is_added_multiple_times() + { + $cart = $this->getCart(); + + $item = new BuyableProduct; + + $cart->add($item); + $cart->add($item); + $cart->add($item); + + $this->assertItemsInCart(3, $cart); + $this->assertRowsInCart(1, $cart); + } + + /** @test */ + public function it_can_update_the_quantity_of_an_existing_item_in_the_cart() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->update('027c91341fd5cf4d2579b49c4b6a90da', 2); + + $this->assertItemsInCart(2, $cart); + $this->assertRowsInCart(1, $cart); + + Event::assertDispatched('cart.updated'); + } + + /** @test */ + public function it_can_update_an_existing_item_in_the_cart_from_a_buyable() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->update('027c91341fd5cf4d2579b49c4b6a90da', new BuyableProduct(1, 'Different description')); + + $this->assertItemsInCart(1, $cart); + $this->assertEquals('Different description', $cart->get('027c91341fd5cf4d2579b49c4b6a90da')->name); + + Event::assertDispatched('cart.updated'); + } + + /** @test */ + public function it_can_update_an_existing_item_in_the_cart_from_an_array() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->update('027c91341fd5cf4d2579b49c4b6a90da', ['name' => 'Different description']); + + $this->assertItemsInCart(1, $cart); + $this->assertEquals('Different description', $cart->get('027c91341fd5cf4d2579b49c4b6a90da')->name); + + Event::assertDispatched('cart.updated'); + } + + /** + * @test + * @expectedException \Gloudemans\Shoppingcart\Exceptions\InvalidRowIDException + */ + public function it_will_throw_an_exception_if_a_rowid_was_not_found() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->update('none-existing-rowid', new BuyableProduct(1, 'Different description')); + } + + /** @test */ + public function it_will_regenerate_the_rowid_if_the_options_changed() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct, 1, ['color' => 'red']); + + $cart->update('ea65e0bdcd1967c4b3149e9e780177c0', ['options' => ['color' => 'blue']]); + + $this->assertItemsInCart(1, $cart); + $this->assertEquals('7e70a1e9aaadd18c72921a07aae5d011', $cart->content()->first()->rowId); + $this->assertEquals('blue', $cart->get('7e70a1e9aaadd18c72921a07aae5d011')->options->color); + } + + /** @test */ + public function it_will_add_the_item_to_an_existing_row_if_the_options_changed_to_an_existing_rowid() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct, 1, ['color' => 'red']); + $cart->add(new BuyableProduct, 1, ['color' => 'blue']); + + $cart->update('7e70a1e9aaadd18c72921a07aae5d011', ['options' => ['color' => 'red']]); + + $this->assertItemsInCart(2, $cart); + $this->assertRowsInCart(1, $cart); + } + + /** @test */ + public function it_can_remove_an_item_from_the_cart() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->remove('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertItemsInCart(0, $cart); + $this->assertRowsInCart(0, $cart); + + Event::assertDispatched('cart.removed'); + } + + /** @test */ + public function it_will_remove_the_item_if_its_quantity_was_set_to_zero() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->update('027c91341fd5cf4d2579b49c4b6a90da', 0); + + $this->assertItemsInCart(0, $cart); + $this->assertRowsInCart(0, $cart); + + Event::assertDispatched('cart.removed'); + } + + /** @test */ + public function it_will_remove_the_item_if_its_quantity_was_set_negative() + { + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->update('027c91341fd5cf4d2579b49c4b6a90da', -1); + + $this->assertItemsInCart(0, $cart); + $this->assertRowsInCart(0, $cart); + + Event::assertDispatched('cart.removed'); + } + + /** @test */ + public function it_can_get_an_item_from_the_cart_by_its_rowid() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertInstanceOf(CartItem::class, $cartItem); + } + + /** @test */ + public function it_can_get_the_content_of_the_cart() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1)); + $cart->add(new BuyableProduct(2)); + + $content = $cart->content(); + + $this->assertInstanceOf(Collection::class, $content); + $this->assertCount(2, $content); + } + + /** @test */ + public function it_will_return_an_empty_collection_if_the_cart_is_empty() + { + $cart = $this->getCart(); + + $content = $cart->content(); + + $this->assertInstanceOf(Collection::class, $content); + $this->assertCount(0, $content); + } + + /** @test */ + public function it_will_include_the_tax_and_subtotal_when_converted_to_an_array() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1)); + $cart->add(new BuyableProduct(2)); + + $content = $cart->content(); + + $this->assertInstanceOf(Collection::class, $content); + $this->assertEquals([ + '027c91341fd5cf4d2579b49c4b6a90da' => [ + 'rowId' => '027c91341fd5cf4d2579b49c4b6a90da', + 'id' => 1, + 'name' => 'Item name', + 'qty' => 1, + 'price' => 10.00, + 'tax' => 2.10, + 'subtotal' => 10.0, + 'isSaved' => false, + 'options' => [], + ], + '370d08585360f5c568b18d1f2e4ca1df' => [ + 'rowId' => '370d08585360f5c568b18d1f2e4ca1df', + 'id' => 2, + 'name' => 'Item name', + 'qty' => 1, + 'price' => 10.00, + 'tax' => 2.10, + 'subtotal' => 10.0, + 'isSaved' => false, + 'options' => [], + ] + ], $content->toArray()); + } + + /** @test */ + public function it_can_destroy_a_cart() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $this->assertItemsInCart(1, $cart); + + $cart->destroy(); + + $this->assertItemsInCart(0, $cart); + } + + /** @test */ + public function it_can_get_the_total_price_of_the_cart_content() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'First item', 10.00)); + $cart->add(new BuyableProduct(2, 'Second item', 25.00), 2); + + $this->assertItemsInCart(3, $cart); + $this->assertEquals(60.00, $cart->subtotal()); + } + + /** @test */ + public function it_can_return_a_formatted_total() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'First item', 1000.00)); + $cart->add(new BuyableProduct(2, 'Second item', 2500.00), 2); + + $this->assertItemsInCart(3, $cart); + $this->assertEquals('6.000,00', $cart->subtotal(2, ',', '.')); + } + + /** @test */ + public function it_can_search_the_cart_for_a_specific_item() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some item')); + $cart->add(new BuyableProduct(2, 'Another item')); + + $cartItem = $cart->search(function ($cartItem, $rowId) { + return $cartItem->name == 'Some item'; + }); + + $this->assertInstanceOf(Collection::class, $cartItem); + $this->assertCount(1, $cartItem); + $this->assertInstanceOf(CartItem::class, $cartItem->first()); + $this->assertEquals(1, $cartItem->first()->id); + } + + /** @test */ + public function it_can_search_the_cart_for_multiple_items() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some item')); + $cart->add(new BuyableProduct(2, 'Some item')); + $cart->add(new BuyableProduct(3, 'Another item')); + + $cartItem = $cart->search(function ($cartItem, $rowId) { + return $cartItem->name == 'Some item'; + }); + + $this->assertInstanceOf(Collection::class, $cartItem); + } + + /** @test */ + public function it_can_search_the_cart_for_a_specific_item_with_options() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some item'), 1, ['color' => 'red']); + $cart->add(new BuyableProduct(2, 'Another item'), 1, ['color' => 'blue']); + + $cartItem = $cart->search(function ($cartItem, $rowId) { + return $cartItem->options->color == 'red'; + }); + + $this->assertInstanceOf(Collection::class, $cartItem); + $this->assertCount(1, $cartItem); + $this->assertInstanceOf(CartItem::class, $cartItem->first()); + $this->assertEquals(1, $cartItem->first()->id); + } + + /** @test */ + public function it_will_associate_the_cart_item_with_a_model_when_you_add_a_buyable() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertContains(BuyableProduct::class, Assert::readAttribute($cartItem, 'associatedModel')); + } + + /** @test */ + public function it_can_associate_the_cart_item_with_a_model() + { + $cart = $this->getCart(); + + $cart->add(1, 'Test item', 1, 10.00); + + $cart->associate('027c91341fd5cf4d2579b49c4b6a90da', new ProductModel); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals(ProductModel::class, Assert::readAttribute($cartItem, 'associatedModel')); + } + + /** + * @test + * @expectedException \Gloudemans\Shoppingcart\Exceptions\UnknownModelException + * @expectedExceptionMessage The supplied model SomeModel does not exist. + */ + public function it_will_throw_an_exception_when_a_non_existing_model_is_being_associated() + { + $cart = $this->getCart(); + + $cart->add(1, 'Test item', 1, 10.00); + + $cart->associate('027c91341fd5cf4d2579b49c4b6a90da', 'SomeModel'); + } + + /** @test */ + public function it_can_get_the_associated_model_of_a_cart_item() + { + $cart = $this->getCart(); + + $cart->add(1, 'Test item', 1, 10.00); + + $cart->associate('027c91341fd5cf4d2579b49c4b6a90da', new ProductModel); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertInstanceOf(ProductModel::class, $cartItem->model); + $this->assertEquals('Some value', $cartItem->model->someValue); + } + + /** @test */ + public function it_can_calculate_the_subtotal_of_a_cart_item() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 9.99), 3); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals(29.97, $cartItem->subtotal); + } + + /** @test */ + public function it_can_return_a_formatted_subtotal() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 500), 3); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals('1.500,00', $cartItem->subtotal(2, ',', '.')); + } + + /** @test */ + public function it_can_calculate_tax_based_on_the_default_tax_rate_in_the_config() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 10.00), 1); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals(2.10, $cartItem->tax); + } + + /** @test */ + public function it_can_calculate_tax_based_on_the_specified_tax() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 10.00), 1); + + $cart->setTax('027c91341fd5cf4d2579b49c4b6a90da', 19); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals(1.90, $cartItem->tax); + } + + /** @test */ + public function it_can_return_the_calculated_tax_formatted() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 10000.00), 1); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals('2.100,00', $cartItem->tax(2, ',', '.')); + } + + /** @test */ + public function it_can_calculate_the_total_tax_for_all_cart_items() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 10.00), 1); + $cart->add(new BuyableProduct(2, 'Some title', 20.00), 2); + + $this->assertEquals(10.50, $cart->tax); + } + + /** @test */ + public function it_can_return_formatted_total_tax() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 1000.00), 1); + $cart->add(new BuyableProduct(2, 'Some title', 2000.00), 2); + + $this->assertEquals('1.050,00', $cart->tax(2, ',', '.')); + } + + /** @test */ + public function it_can_return_the_subtotal() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 10.00), 1); + $cart->add(new BuyableProduct(2, 'Some title', 20.00), 2); + + $this->assertEquals(50.00, $cart->subtotal); + } + + /** @test */ + public function it_can_return_formatted_subtotal() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 1000.00), 1); + $cart->add(new BuyableProduct(2, 'Some title', 2000.00), 2); + + $this->assertEquals('5000,00', $cart->subtotal(2, ',', '')); + } + + /** @test */ + public function it_can_return_cart_formated_numbers_by_config_values() + { + $this->setConfigFormat(2, ',', ''); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 1000.00), 1); + $cart->add(new BuyableProduct(2, 'Some title', 2000.00), 2); + + $this->assertEquals('5000,00', $cart->subtotal()); + $this->assertEquals('1050,00', $cart->tax()); + $this->assertEquals('6050,00', $cart->total()); + + $this->assertEquals('5000,00', $cart->subtotal); + $this->assertEquals('1050,00', $cart->tax); + $this->assertEquals('6050,00', $cart->total); + } + + /** @test */ + public function it_can_return_cartItem_formated_numbers_by_config_values() + { + $this->setConfigFormat(2, ',', ''); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 2000.00), 2); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals('2000,00', $cartItem->price()); + $this->assertEquals('2420,00', $cartItem->priceTax()); + $this->assertEquals('4000,00', $cartItem->subtotal()); + $this->assertEquals('4840,00', $cartItem->total()); + $this->assertEquals('420,00', $cartItem->tax()); + $this->assertEquals('840,00', $cartItem->taxTotal()); + } + + /** @test */ + public function it_can_store_the_cart_in_a_database() + { + $this->artisan('migrate', [ + '--database' => 'testing', + ]); + + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->store($identifier = 123); + + $serialized = serialize($cart->content()); + + $this->assertDatabaseHas('shoppingcart', ['identifier' => $identifier, 'instance' => 'default', 'content' => $serialized]); + + Event::assertDispatched('cart.stored'); + } + + /** @test */ + public function it_can_update_the_cart_in_database() + { + $this->artisan('migrate', [ + '--database' => 'testing', + ]); + + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->store($identifier = 123); + + $serialized = serialize($cart->content()); + + $this->assertDatabaseHas('shoppingcart', ['identifier' => $identifier, 'instance' => 'default', 'content' => $serialized]); + + Event::assertDispatched('cart.stored'); + } + + /** @test */ + public function it_can_restore_a_cart_from_the_database() + { + $this->artisan('migrate', [ + '--database' => 'testing', + ]); + + Event::fake(); + + $cart = $this->getCart(); + + $cart->add(new BuyableProduct); + + $cart->store($identifier = 123); + + $cart->destroy(); + + $this->assertItemsInCart(0, $cart); + + $cart->restore($identifier); + + $this->assertItemsInCart(1, $cart); + + Event::assertDispatched('cart.restored'); + } + + /** @test */ + public function it_will_just_keep_the_current_instance_if_no_cart_with_the_given_identifier_was_stored() + { + $this->artisan('migrate', [ + '--database' => 'testing', + ]); + + $cart = $this->getCart(); + + $cart->restore($identifier = 123); + + $this->assertItemsInCart(0, $cart); + } + + /** @test */ + public function it_can_calculate_all_values() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'First item', 10.00), 2); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $cart->setTax('027c91341fd5cf4d2579b49c4b6a90da', 19); + + $this->assertEquals(10.00, $cartItem->price(2)); + $this->assertEquals(11.90, $cartItem->priceTax(2)); + $this->assertEquals(20.00, $cartItem->subtotal(2)); + $this->assertEquals(23.80, $cartItem->total(2)); + $this->assertEquals(1.90, $cartItem->tax(2)); + $this->assertEquals(3.80, $cartItem->taxTotal(2)); + + $this->assertEquals(20.00, $cart->subtotal(2)); + $this->assertEquals(23.80, $cart->total(2)); + $this->assertEquals(3.80, $cart->tax(2)); + } + + /** @test */ + public function it_will_destroy_the_cart_when_the_user_logs_out_and_the_config_setting_was_set_to_true() + { + $this->app['config']->set('cart.destroy_on_logout', true); + + $this->app->instance(SessionManager::class, Mockery::mock(SessionManager::class, function ($mock) { + $mock->shouldReceive('forget')->once()->with('cart'); + })); + + $user = Mockery::mock(Authenticatable::class); + + $guard = $this->app->make('auth'); + + event(new Logout($guard, $user)); + } + + /** + * Get an instance of the cart. + * + * @return \Gloudemans\Shoppingcart\Cart + */ + private function getCart() + { + $session = $this->app->make('session'); + $events = $this->app->make('events'); + + return new Cart($session, $events); + } + + /** + * Set the config number format. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + */ + private function setConfigFormat($decimals, $decimalPoint, $thousandSeperator) + { + $this->app['config']->set('cart.format.decimals', $decimals); + $this->app['config']->set('cart.format.decimal_point', $decimalPoint); + $this->app['config']->set('cart.format.thousand_seperator', $thousandSeperator); + } +} diff --git a/packages/digital-bird/shoppingcart/tests/Fixtures/BuyableProduct.php b/packages/digital-bird/shoppingcart/tests/Fixtures/BuyableProduct.php new file mode 100644 index 0000000..a58e830 --- /dev/null +++ b/packages/digital-bird/shoppingcart/tests/Fixtures/BuyableProduct.php @@ -0,0 +1,67 @@ +id = $id; + $this->name = $name; + $this->price = $price; + } + + /** + * Get the identifier of the Buyable item. + * + * @return int|string + */ + public function getBuyableIdentifier($options = null) + { + return $this->id; + } + + /** + * Get the description or title of the Buyable item. + * + * @return string + */ + public function getBuyableDescription($options = null) + { + return $this->name; + } + + /** + * Get the price of the Buyable item. + * + * @return float + */ + public function getBuyablePrice($options = null) + { + return $this->price; + } +} \ No newline at end of file diff --git a/packages/digital-bird/shoppingcart/tests/Fixtures/ProductModel.php b/packages/digital-bird/shoppingcart/tests/Fixtures/ProductModel.php new file mode 100644 index 0000000..70494ca --- /dev/null +++ b/packages/digital-bird/shoppingcart/tests/Fixtures/ProductModel.php @@ -0,0 +1,13 @@ +=7.2.0", + "php": ">=8.1", "ext-exif": "*", "ext-fileinfo": "*", "intervention/image": "2.*", - "illuminate/config": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/filesystem": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/support": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/http": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", - "illuminate/container": "5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0 || ^8.0", + "illuminate/config": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/filesystem": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "cviebrock/eloquent-sluggable": "*" }, diff --git a/packages/iqcontent/laravel-filemanager/src/Lfm.php b/packages/iqcontent/laravel-filemanager/src/Lfm.php index bf6103d..edf430d 100644 --- a/packages/iqcontent/laravel-filemanager/src/Lfm.php +++ b/packages/iqcontent/laravel-filemanager/src/Lfm.php @@ -342,7 +342,7 @@ class Lfm ]); Route::get('/cropnewimage', [ 'uses' => 'CropController@getNewCropimage', - 'as' => 'getCropimage', + 'as' => 'getNewCropimage', ]); // add-file @@ -360,13 +360,13 @@ class Lfm // file-content Route::get('/file-content', [ 'uses' => 'FileController@fileContent', - 'as' => 'getFile', + 'as' => 'getFileContent', ]); // rename Route::get('/rename', [ 'uses' => 'RenameController@getRename', - 'as' => 'getFile', + 'as' => 'getRename', ]); // colorshue diff --git a/packages/iqcontent/laravel-filemanager/src/LfmStorageRepository.php b/packages/iqcontent/laravel-filemanager/src/LfmStorageRepository.php index 4740856..3b009bb 100644 --- a/packages/iqcontent/laravel-filemanager/src/LfmStorageRepository.php +++ b/packages/iqcontent/laravel-filemanager/src/LfmStorageRepository.php @@ -3,7 +3,6 @@ namespace IqContent\LaravelFilemanager; use Illuminate\Support\Facades\Storage; -use League\Flysystem\Cached\CachedAdapter; class LfmStorageRepository { @@ -26,13 +25,7 @@ class LfmStorageRepository public function rootPath() { - $adapter = $this->disk->getDriver()->getAdapter(); - - if ($adapter instanceof CachedAdapter) { - $adapter = $adapter->getAdapter(); - } - - return $adapter->getPathPrefix(); + return $this->disk->path(''); } public function move($new_lfm_path) diff --git a/phpunit.xml b/phpunit.xml index 9ee3e73..ae26ca2 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,7 @@ + colors="true"> ./tests/Feature @@ -17,16 +11,25 @@ ./tests/Unit - - + + ./app - - +
+
- - + + + + + + + + + + + - - + + diff --git a/public/storage b/public/storage index 3ab7fef..c79c50c 120000 --- a/public/storage +++ b/public/storage @@ -1 +1 @@ -/Users/kadmin/Websites/mein.sterntours.local/storage/app/public \ No newline at end of file +/workspace/mein.sterntours.de/storage/app/public \ No newline at end of file diff --git a/resources/views/contact/_detail_contact.blade.php b/resources/views/contact/_detail_contact.blade.php new file mode 100644 index 0000000..852af81 --- /dev/null +++ b/resources/views/contact/_detail_contact.blade.php @@ -0,0 +1,135 @@ +
+ + +
+
+ + @if($id !== 'new') +
+
+ + {{ Form::text('contact_id', $contact->id, ['placeholder' => __('Kontakt ID'), 'class' => 'form-control', 'id' => 'contact_id', 'readonly']) }} +
+
+ + +
+
+ @endif + +
+
+ + {{ Form::select('salutation_id', \App\Models\Contact::$salutationType, $contact->salutation_id, ['class' => 'custom-select', 'required' => true]) }} +
+ +
+ + {{ Form::text('firstname', $contact->firstname, ['placeholder' => __('Vorname'), 'class' => 'form-control', 'id' => 'firstname', 'required' => true]) }} +
+ +
+ + {{ Form::text('name', $contact->name, ['placeholder' => __('Nachname'), 'class' => 'form-control', 'id' => 'name', 'required' => true]) }} +
+ +
+ + {{ Form::text('birthdate', $contact->birthdate ? $contact->birthdate->format('d.m.Y') : '', ['placeholder' => 'TT.MM.JJJJ', 'class' => 'form-control', 'id' => 'birthdate']) }} +
+ +
+ + {{ Form::text('company', $contact->company, ['placeholder' => __('Firma'), 'class' => 'form-control', 'id' => 'company']) }} +
+ +
+ + {{ Form::text('street', $contact->street, ['placeholder' => __('Straße'), 'class' => 'form-control', 'id' => 'street']) }} +
+ +
+ + {{ Form::text('zip', $contact->zip, ['placeholder' => __('PLZ'), 'class' => 'form-control', 'id' => 'zip']) }} +
+ +
+ + {{ Form::text('city', $contact->city, ['placeholder' => __('Stadt'), 'class' => 'form-control', 'id' => 'city']) }} +
+ +
+ + {{ Form::select('country_id', \App\Models\Contact::getCountriesArray(), $contact->country_id, ['class' => 'custom-select', 'required' => true]) }} +
+ +
+ + {{ Form::text('phone', $contact->phone, ['placeholder' => __('Telefon'), 'class' => 'form-control', 'id' => 'phone']) }} +
+ +
+ + {{ Form::text('phonemobile', $contact->phonemobile, ['placeholder' => __('Mobil'), 'class' => 'form-control', 'id' => 'phonemobile']) }} +
+ +
+ + {{ Form::text('phonebusiness', $contact->phonebusiness, ['placeholder' => __('Geschäftlich'), 'class' => 'form-control', 'id' => 'phonebusiness']) }} +
+ +
+ + {{ Form::text('email', $contact->email, ['placeholder' => __('E-Mail'), 'class' => 'form-control', 'id' => 'email', 'required' => true]) }} +
+ +
+
+ + + {{ __('zur Übersicht') }} + +
+
+
+ + @if($id !== 'new' && $contact->mergedContacts->isNotEmpty()) +
+
{{ __('Zusammengeführte Duplikate') }}
+ + + + + + + + + + + @foreach($contact->mergedContacts as $merged) + + + + + + + @endforeach + +
{{ __('ID') }}{{ __('Name') }}{{ __('E-Mail') }}{{ __('Zusammengeführt am') }}
{{ $merged->id }}{{ $merged->fullName() }}{{ $merged->email }}{{ $merged->merged_at ? $merged->merged_at->format('d.m.Y') : '-' }}
+ @endif + +
+
+
diff --git a/resources/views/contact/_detail_history.blade.php b/resources/views/contact/_detail_history.blade.php new file mode 100644 index 0000000..e232490 --- /dev/null +++ b/resources/views/contact/_detail_history.blade.php @@ -0,0 +1,122 @@ +@php $modal = $modal ?? false; @endphp + +@if(!$modal) +
+ +
+
+@endif + + {{-- Anfragen --}} + @if($contact->leads->isNotEmpty()) +
{{ __('Anfragen') }}
+
+ + + + + + + + + + + @foreach($contact->leads as $lead) + + + + + + + @endforeach + +
{{ __('ID') }}{{ __('Sachbearbeiter') }}{{ __('Status') }}{{ __('Anfrage-Datum') }}
+ + + + {{ $lead->id }} + + @if($lead->sf_guard_user_id && $lead->sf_guard_user) + {{ $lead->sf_guard_user->first_name }} {{ $lead->sf_guard_user->last_name }} + @endif + {!! $lead->getStatusBadge() !!}{{ _format_date($lead->request_date) }}
+
+ @else +

{{ __('Keine Anfragen vorhanden.') }}

+ @endif + +
+ + {{-- Buchungen --}} + @if($contact->bookings->isNotEmpty()) +
{{ __('Buchungen') }}
+
+ + + + + + + + + + + + + + + @foreach($contact->bookings as $booking) + + + + + + + + + + + @endforeach + +
{{ __('ID') }}{{ __('Reiseland') }}{{ __('Programm') }}{{ __('Anreise') }}{{ __('Abreise') }}{{ __('Sachbearbeiter') }}{{ __('Status') }}{{ __('Datum') }}
+ + + + {{ $booking->id }} + + @if($booking->travel_country_id && $booking->travel_country) + {{ $booking->travel_country->name }} + @endif + + @if($booking->travelagenda_id && $booking->travel_agenda) + {{ $booking->travel_agenda->name }} + @endif + {{ _format_date($booking->start_date) }}{{ _format_date($booking->end_date) }} + @if($booking->sf_guard_user_id && $booking->sf_guard_user) + {{ $booking->sf_guard_user->first_name }} {{ $booking->sf_guard_user->last_name }} + @endif + + @if($booking->lead) + {!! $booking->lead->getStatusBadge($booking) !!} + @endif + {{ _format_date($booking->booking_date) }}
+
+ @else +

{{ __('Keine Buchungen vorhanden.') }}

+ @endif + +@if(!$modal) +
+
+
+@endif diff --git a/resources/views/contact/detail.blade.php b/resources/views/contact/detail.blade.php new file mode 100644 index 0000000..d03cb5b --- /dev/null +++ b/resources/views/contact/detail.blade.php @@ -0,0 +1,97 @@ +@extends('layouts.layout-2') + +@section('content') + + + +

+ {{ $id === 'new' ? __('Neuer Kontakt') : __('Kontakt') . ': ' . $contact->fullName() }} +

+ + @if(session('alert-save')) + + @endif + + + + {!! Form::open(['url' => route('contact_detail_store', [$id]), 'class' => 'form-horizontal', 'id' => 'contact-form-validation']) !!} + + + @include('contact._detail_contact') + + @if($id !== 'new') + @include('contact._detail_history') + @endif + + + + {!! Form::close() !!} + + + +@endsection diff --git a/resources/views/contact/duplicates.blade.php b/resources/views/contact/duplicates.blade.php new file mode 100644 index 0000000..3cc5be9 --- /dev/null +++ b/resources/views/contact/duplicates.blade.php @@ -0,0 +1,224 @@ +@extends('layouts.layout-2') + +@section('content') +
+

+ Mögliche Duplikate + Kontakte zur manuellen Prüfung +

+ + Zurück zur Übersicht + +
+ + {{-- Konfidenz-Tabs --}} + + + {{-- Lade-Spinner --}} +
+ +

Duplikate werden geladen…

+
+ + {{-- Gruppen-Container --}} + + + {{-- Leer-Zustand --}} + + + {{-- Erfolg-Toast --}} + + + {{-- Fehler-Toast --}} + + + +@endsection diff --git a/resources/views/contact/index.blade.php b/resources/views/contact/index.blade.php new file mode 100644 index 0000000..b327d3d --- /dev/null +++ b/resources/views/contact/index.blade.php @@ -0,0 +1,409 @@ +@extends('layouts.layout-2') + +@section('content') +
+

+ {{ __('Kontakte') }} + Stammkunden ohne Duplikate +

+ + {{ __('Neuer Kontakt') }} + +
+ + {{-- Filterleiste --}} +
+
+
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + + +
+
+ +
+ +
+ +
+
+
+ + {{-- History-Modal (Anfragen + Buchungen) --}} + + + {{-- Bestätigungs-Modal Löschen --}} + + + {{-- Fehler-Toast --}} + + + {{-- Erfolg-Toast (Wiederherstellen) --}} + + + {{-- Tabelle --}} +
+
+
+
+
+ + + + + + + + + + + + + + + + +
 {{ __('ID') }}{{ __('Vorname') }}{{ __('Nachname') }}{{ __('E-Mail') }}{{ __('PLZ') }}{{ __('Ort') }}{{ __('Anfragen') }}{{ __('Buchungen') }}{{ __('Gelöscht am') }} 
+
+
+ + +@endsection diff --git a/resources/views/customer/mail/modal-show-mail-inner.blade.php b/resources/views/customer/mail/modal-show-mail-inner.blade.php index 0c4eaf2..bff0511 100644 --- a/resources/views/customer/mail/modal-show-mail-inner.blade.php +++ b/resources/views/customer/mail/modal-show-mail-inner.blade.php @@ -45,7 +45,7 @@

Kunde: {{ $customer_mail->customer->salutation->name }} {{ $customer_mail->customer->title }} {{ $customer_mail->customer->firstname }} {{ $customer_mail->customer->name }} @if($customer_mail->booking) - ({{$customer_mail->booking->lead_id}}) + ({{$customer_mail->booking->inquiry_id}}) @endif

@endif diff --git a/resources/views/layouts/application.blade.php b/resources/views/layouts/application.blade.php index 5e901f5..ea18e06 100755 --- a/resources/views/layouts/application.blade.php +++ b/resources/views/layouts/application.blade.php @@ -1,11 +1,13 @@ + - + {{ config('app.name') }} @@ -32,9 +34,9 @@ - @if(isset($lfm_helper)) + @if (isset($lfm_helper)) @else - {{-- --}} + {{-- --}} @endif @@ -42,7 +44,8 @@ - + @@ -65,16 +68,16 @@ -@yield('styles') + @yield('styles') - + + -@yield('layout-content') + @yield('layout-content') -@include('iq.content.assets.modals') + @include('iq.content.assets.modals') - - - - - - - - + + + + + + + + - + -@if(isset($lfm_helper)) -@else - {{-- --}} -@endif + @if (isset($lfm_helper)) + @else + {{-- --}} + @endif - + - - - + + + - - - + - - - - - - - + + + + + + + - - + + -@if(isset($lfm_helper)) - - - - - + @if (isset($lfm_helper)) + + + + + + @endif -@endif + + + - - - + @yield('scripts') -@yield('scripts') - - - - + + + -@include('asset.js') + @include('asset.js') - + $('.summernote-small').summernote({ + height: 150, + tabsize: 2, + followingToolbar: true, + imageTitle: { + specificAltField: true, + }, + toolbar: [ + ['style', ['style']], + ['font', ['bold', 'italic', 'underline', 'clear']], + ['color', ['color']], + ['para', ['ul', 'ol', 'paragraph']], + ['extensions', ['gallery']], + ['insert', ['link', 'picture', 'video', 'hr']], + ['view', ['fullscreen', 'codeview']], + ['help', ['help']] + ], + popover: { + image: [ + ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']], + ['float', ['floatLeft', 'floatRight', 'floatNone']], + ['remove', ['removeMedia']], + ['custom', ['imageTitle']], + ], + }, + callbacks: { + onInit: function() { + // $(this).data('image_dialog_images_html', '
', // Summernote's default is to use '


' + notStyle: 'position:absolute;top:0;left:0;right:0', // Position of Notification + icon: '[Your Button]', + keepHtml: false, // Remove all Html formats + keepOnlyTags: ['

', '
', '

diff --git a/resources/views/pdf/booking_confirmation.blade.php b/resources/views/pdf/booking_confirmation.blade.php index 022b9ae..4924e74 100644 --- a/resources/views/pdf/booking_confirmation.blade.php +++ b/resources/views/pdf/booking_confirmation.blade.php @@ -1,8 +1,6 @@ @extends('pdf.layout-template') @section('content') - - @include('pdf.components.booking_header') @include('pdf.components.booking_participant') @@ -13,7 +11,5 @@ @include('pdf.components.booking_sterntours') - @include('pdf.components.booking_footer', ['set_contact_footer' => false]) - - + @include('pdf.components.booking_footer', ['set_contact_footer' => true]) @endsection diff --git a/resources/views/pdf/booking_registration.blade.php b/resources/views/pdf/booking_registration.blade.php index 7d4ff15..82f8d98 100644 --- a/resources/views/pdf/booking_registration.blade.php +++ b/resources/views/pdf/booking_registration.blade.php @@ -1,8 +1,6 @@ @extends('pdf.layout-template') @section('content') - - @include('pdf.components.booking_header') @include('pdf.components.booking_participant') @@ -15,7 +13,5 @@ @include('pdf.components.booking_sterntours') - @include('pdf.components.booking_footer', ['set_contact_footer' => false]) - - + @include('pdf.components.booking_footer', ['set_contact_footer' => true]) @endsection diff --git a/resources/views/pdf/components/booking_head.blade.php b/resources/views/pdf/components/booking_head.blade.php index 50ea257..d043bb9 100644 --- a/resources/views/pdf/components/booking_head.blade.php +++ b/resources/views/pdf/components/booking_head.blade.php @@ -22,7 +22,7 @@ {{-- @endif --}} - Buchungsnummer: {{ $booking->lead_id }}
+ Buchungsnummer: {{ $booking->inquiry_id }}
Buchungsdatum: {{ _format_date($booking->booking_date) }}

Reisetermin: {{ _format_date($booking->start_date) }} - {{ _format_date($booking->end_date) }}
diff --git a/resources/views/pdf/components/booking_header.blade.php b/resources/views/pdf/components/booking_header.blade.php index 89a44c6..265985e 100644 --- a/resources/views/pdf/components/booking_header.blade.php +++ b/resources/views/pdf/components/booking_header.blade.php @@ -35,7 +35,7 @@ Buchungsnummer: - {{ $booking->lead_id }} + {{ $booking->inquiry_id }} Buchungsdatum: diff --git a/routes/web.php b/routes/web.php index f59bec6..49c982e 100755 --- a/routes/web.php +++ b/routes/web.php @@ -11,11 +11,15 @@ | */ +use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Redirect; Auth::routes([ 'register' => false, // Registration Routes... //'reset' => false, // Password Reset Routes... - 'verify' => false, // Email Verification Routes... + 'verify' => false, // Email Verification Routes... + 'logout' => false, // Custom logout with Google2FA below ]); Route::get('/logout', function () { @@ -42,7 +46,7 @@ Route::get('/imprint', 'HomeController@legalImprint')->name('imprint'); Route::post('/loading/modal', 'HomeController@loadingModal')->name('loading_modal'); -Route::get('/', 'HomeController@index')->name('home'); +Route::get('/', 'HomeController@index')->name('welcome'); Route::get('/check/login/{identify}/{token}/{show?}', 'HomeController@checkLogin')->name('check_login'); @@ -60,19 +64,19 @@ Route::group(['middleware' => ['auth', '2fa']], function () { Route::group(['middleware' => ['auth.permission:my-dat']], function () { Route::get('/user/edit', 'UserDataController@userEdit')->name('user_edit'); - Route::post('/user/edit', 'UserDataController@userEditStore')->name('user_edit'); + Route::post('/user/edit', 'UserDataController@userEditStore')->name('user_edit_store'); Route::get('/user/update_password', 'UserUpdatePasswordController@updatePassword')->name('user_update_password'); - Route::post('/user/update_password', 'UserUpdatePasswordController@updatePasswordStore')->name('user_update_password'); + Route::post('/user/update_password', 'UserUpdatePasswordController@updatePasswordStore')->name('user_update_password_store'); Route::get('/user/update_password_first', 'UserUpdatePasswordController@updatePasswordFirst')->name('user_update_password_first'); - Route::post('/user/update_password_first', 'UserUpdatePasswordController@updatePasswordFirstStore')->name('user_update_password_first'); + Route::post('/user/update_password_first', 'UserUpdatePasswordController@updatePasswordFirstStore')->name('user_update_password_first_store'); Route::get('/user/update_email', 'UserUpdateEmailController@index')->name('user_update_email'); - Route::post('/user/update_email', 'UserUpdateEmailController@update')->name('user_update_email'); + Route::post('/user/update_email', 'UserUpdateEmailController@update')->name('user_update_email_store'); Route::get('/user/delete_account', 'UserDeleteController@deleteAccount')->name('user_delete_account'); - Route::post('/user/delete_account', 'UserDeleteController@deleteAccountAction')->name('user_delete_account'); + Route::post('/user/delete_account', 'UserDeleteController@deleteAccountAction')->name('user_delete_account_store'); Route::post('/user/data/accepted/form', 'UserDataController@userDataAcceptedForm')->name('user_data_accepted_form'); }); @@ -88,12 +92,11 @@ Route::group(['middleware' => ['admin', '2fa']], function () { //trees Route::get('/iq/content/tree/index', 'IQ\ContentTreeController@index')->name('iq_content_tree_index'); Route::get('/iq/content/tree/detail/{id}/{node_id?}/{area_section_id?}', 'IQ\ContentTreeController@detail')->name('iq_content_tree_detail'); - Route::post('/iq/content/tree/detail/{id}/{node_id?}/{area_section_id?}', 'IQ\ContentTreeController@store')->name('iq_content_tree_detail'); + Route::post('/iq/content/tree/detail/{id}/{node_id?}/{area_section_id?}', 'IQ\ContentTreeController@store')->name('iq_content_tree_detail_store'); Route::get('/iq/content/tree/detail/remove/{action}/{id}/{node_id}/{remove_id}/{r?}', 'IQ\ContentTreeController@remove')->name('iq_content_tree_detail_remove'); Route::get('/iq/content/tree/delete/{id}/{node_id?}/{area_section_id?}', 'IQ\ContentTreeController@delete')->name('iq_content_tree_delete'); Route::get('/iq/content/tree/repair/{id}', 'IQ\ContentTreeController@repair')->name('iq_content_tree_repair'); - Route::post('/iq/content/modal/load', 'IQ\ContentModalController@load')->name('iq_content_modal_load'); Route::post('/customer_mail/upload/attachment/{id}', 'CustomerMailController@uploadAttachment')->name('customer_mail_upload_attachment'); Route::post('/customer_mail/send/mail', 'CustomerMailController@sendMail')->name('customer_mail_send_mail'); @@ -101,10 +104,9 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('/customer_mail/data/table', 'CustomerMailController@getRequests')->name('customer_mail_data_table'); Route::get('/email_template/data/table', 'CustomerMailController@getEmailTemplates')->name('email_template_data_table'); Route::get('/customer_mail/delete/{id}', 'CustomerMailController@delete')->name('customer_mail_delete'); - Route::get('/customer_mail/delete/{id}', 'CustomerMailController@delete')->name('customer_mail_delete'); Route::post('customer_mail/ajax', 'CustomerMailController@ajax')->name('customer_mail_ajax'); Route::get('/customer_mail/detail/{id}', 'CustomerMailController@detail')->name('customer_mail_detail'); - Route::post('/customer_mail/detail/{id}/{action?}', 'CustomerMailController@store')->name('customer_mail_detail'); + Route::post('/customer_mail/detail/{id}/{action?}', 'CustomerMailController@store')->name('customer_mail_detail_store'); Route::post('/customer_fewo_mail/upload/attachment/{id}', 'CustomerFewoMailController@uploadAttachment')->name('customer_fewo_mail_upload_attachment'); Route::post('/customer_fewo_mail/send/mail', 'CustomerFewoMailController@sendMail')->name('customer_fewo_mail_send_mail'); @@ -150,7 +152,7 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('/travel/programs/{step?}', 'TravelProgramController@index')->name('travel_programs'); Route::get('/travel/program/detail/{id}', 'TravelProgramController@detail')->name('travel_program_detail'); - Route::post('/travel/program/detail/{id}', 'TravelProgramController@store')->name('travel_program_detail'); + Route::post('/travel/program/detail/{id}', 'TravelProgramController@store')->name('travel_program_detail_store'); Route::get('/travel/program/generate_keywords/{id}', 'TravelProgramController@generateKeywords')->name('travel_program_generate_keywords'); Route::post('/travel/program/class/update', 'TravelProgramController@classUpdate')->name('travel_program_class_update'); @@ -164,7 +166,7 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('/drafts/{step?}', 'DraftController@index')->name('drafts'); Route::get('/draft/detail/{id}', 'DraftController@detail')->name('draft_detail'); - Route::post('/draft/detail/{id}', 'DraftController@store')->name('draft_detail'); + Route::post('/draft/detail/{id}', 'DraftController@store')->name('draft_detail_store'); Route::get('/draft/item/delete/{id}', 'DraftController@itemDelete')->name('draft_item_delete'); Route::get('/draft/delete/{id}', 'DraftController@delete')->name('draft_delete'); @@ -173,10 +175,10 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('/draft/type/delete/{id}', 'DraftController@typeDelete')->name('draft_type_delete'); Route::get('/draft/load/new', 'DraftController@loadNew')->name('draft_load_new'); - Route::post('/draft/load/new', 'DraftController@loadNewAction')->name('draft_load_new'); + Route::post('/draft/load/new', 'DraftController@loadNewAction')->name('draft_load_new_store'); Route::get('/draft/load/old', 'DraftController@loadOld')->name('draft_load_old'); - Route::post('/draft/load/old', 'DraftController@loadOldAction')->name('draft_load_old'); + Route::post('/draft/load/old', 'DraftController@loadOldAction')->name('draft_load_old_store'); }); Route::group(['middleware' => ['auth.permission:crm-tp-tc']], function () { @@ -184,26 +186,26 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('/travel_content/{step?}', 'TravelContentController@index')->name('travel_content'); Route::get('/travel_content/detail/{id}', 'TravelContentController@detail')->name('travel_content_detail'); - Route::post('/travel_content/detail/{id}', 'TravelContentController@store')->name('travel_content_detail'); + Route::post('/travel_content/detail/{id}', 'TravelContentController@store')->name('travel_content_detail_store'); Route::get('/travel_content/sub_detail/{id}', 'TravelContentController@subDetail')->name('travel_content_sub_detail'); - Route::post('/travel_content/sub_detail/{id}', 'TravelContentController@subStore')->name('travel_content_sub_detail'); + Route::post('/travel_content/sub_detail/{id}', 'TravelContentController@subStore')->name('travel_content_sub_detail_store'); Route::post('/travel_content/load', 'TravelContentController@load')->name('travel_content_load'); Route::post('/travel_content/update', 'TravelContentController@update')->name('travel_content_update'); - /*Route::get('/draft/item/delete/{id}', 'DraftController@itemDelete')->name('draft_item_delete'); - Route::get('/draft/delete/{id}', 'DraftController@delete')->name('draft_delete'); + /*Route::get('/draft/item/delete/{id}', 'DraftController@itemDelete')->name('draft_item_delete_2'); + Route::get('/draft/delete/{id}', 'DraftController@delete')->name('draft_delete_2'); - Route::post('/draft/type/update', 'DraftController@typeUpdate')->name('draft_type_update'); - Route::get('/draft/type/delete/{id}', 'DraftController@typeDelete')->name('draft_type_delete'); + Route::post('/draft/type/update', 'DraftController@typeUpdate')->name('draft_type_update_2'); + Route::get('/draft/type/delete/{id}', 'DraftController@typeDelete')->name('draft_type_delete_2'); - Route::get('/draft/load/new', 'DraftController@loadNew')->name('draft_load_new'); - Route::post('/draft/load/new', 'DraftController@loadNewAction')->name('draft_load_new'); + Route::get('/draft/load/new', 'DraftController@loadNew')->name('draft_load_new_2'); + Route::post('/draft/load/new', 'DraftController@loadNewAction')->name('draft_load_new_store'); - Route::get('/draft/load/old', 'DraftController@loadOld')->name('draft_load_old'); - Route::post('/draft/load/old', 'DraftController@loadOldAction')->name('draft_load_old');*/ + Route::get('/draft/load/old', 'DraftController@loadOld')->name('draft_load_old_2'); + Route::post('/draft/load/old', 'DraftController@loadOldAction')->name('draft_load_old_store');*/ }); @@ -221,11 +223,11 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('data/table/bookings', 'DataTableController@getBookings')->name('data_table_bookings'); Route::get('/bookings/{step?}', 'BookingController@index')->name('bookings'); Route::get('/booking/detail/{id}', 'BookingController@detail')->name('booking_detail'); - Route::post('/booking/detail/{id}', 'BookingController@store')->name('booking_detail'); + Route::post('/booking/detail/{id}', 'BookingController@store')->name('booking_detail_store'); Route::get('/booking/draft_item/delete/{id}', 'BookingController@draftItemDelete')->name('booking_draft_item_delete'); Route::post('/booking/modal/load', 'BookingController@loadModal')->name('booking_modal_load'); Route::get('/booking/action/{action}/{id?}', 'BookingController@action')->name('booking_action'); - Route::post('/booking/action/{action}/{id?}', 'BookingController@action')->name('booking_action'); + Route::post('/booking/action/{action}/{id?}', 'BookingController@action')->name('booking_action_store'); Route::get('/booking/delete/{id}/{del?}', 'BookingController@delete')->name('booking_delete'); Route::post('/booking/delete/complete/{id}', 'BookingController@deleteComplete')->name('booking_delete_complete'); }); @@ -234,20 +236,32 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('data/table/leads', 'LeadController@getLeads')->name('data_table_leads'); Route::get('/leads/{step?}', 'LeadController@index')->name('leads'); Route::get('/lead/detail/{id}', 'LeadController@detail')->name('lead_detail'); - Route::post('/lead/detail/{id}', 'LeadController@store')->name('lead_detail'); + Route::post('/lead/detail/{id}', 'LeadController@store')->name('lead_detail_store'); Route::post('/lead/modal/load', 'LeadController@loadModal')->name('lead_modal_load'); Route::get('/lead/action/{action}/{id?}', 'LeadController@action')->name('lead_action'); - Route::post('/lead/action/{action}/{id?}', 'LeadController@action')->name('lead_action'); + Route::post('/lead/action/{action}/{id?}', 'LeadController@action')->name('lead_action_store'); Route::post('lead/ajax/requests', 'LeadController@getAjaxRequests')->name('lead_ajax_requests'); Route::get('/lead/delete/{id}/{del?}', 'LeadController@delete')->name('lead_delete'); }); Route::group(['middleware' => ['auth.permission:crm-bo-cu']], function () { - //Buchungen > Kunden + //Buchungen > Kunden (alt — bleibt erhalten) Route::get('data/table/customers', 'CustomerController@getCustomers')->name('data_table_customers'); Route::get('/customers/{step?}', 'CustomerController@index')->name('customers'); Route::get('/customer/detail/{id}', 'CustomerController@detail')->name('customer_detail'); - Route::post('/customer/detail/{id}', 'CustomerController@store')->name('customer_detail'); + Route::post('/customer/detail/{id}', 'CustomerController@store')->name('customer_detail_store'); Route::get('/customer/delete/{id}', 'CustomerController@delete')->name('customer_delete'); + + //Kontakte (neu) + Route::get('data/table/contacts', 'ContactController@getContacts')->name('data_table_contacts'); + Route::get('/contacts', 'ContactController@index')->name('contacts'); + Route::get('/contacts/duplicates', 'ContactController@duplicates')->name('contacts_duplicates'); + Route::get('/data/contacts/duplicates', 'ContactController@getDuplicateGroups')->name('data_contacts_duplicates'); + Route::post('/contact/merge', 'ContactController@merge')->name('contact_merge'); + Route::get('/contact/detail/{id}', 'ContactController@detail')->name('contact_detail'); + Route::post('/contact/detail/{id}', 'ContactController@store')->name('contact_detail_store'); + Route::delete('/contact/{id}', 'ContactController@destroy')->name('contact_destroy'); + Route::patch('/contact/{id}/restore', 'ContactController@restore')->name('contact_restore'); + Route::get('/contact/{id}/history', 'ContactController@history')->name('contact_history'); }); Route::group(['middleware' => ['auth.permission:crm-mail']], function () { //Emails > Anfragen / Buchungen / Fewo @@ -264,7 +278,7 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('/travel_users', 'TravelUserController@index')->name('travel_users'); Route::get('/data/table/travel_users', 'TravelUserController@getTravelUsers')->name('data_table_travel_users'); Route::get('/travel_user/detail/{id}', 'TravelUserController@detail')->name('travel_user_detail'); - Route::post('/travel_user/detail/{id}', 'TravelUserController@store')->name('travel_user_detail'); + Route::post('/travel_user/detail/{id}', 'TravelUserController@store')->name('travel_user_detail_store'); Route::get('/travel_user/delete/{id}', 'TravelUserController@delete')->name('travel_user_delete'); }); Route::group(['middleware' => ['auth.permission:crm-cm-bf']], function () { @@ -272,7 +286,7 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::get('/travel_user_booking_fewos', 'TravelUserBookingFewoController@index')->name('travel_user_booking_fewos'); Route::get('/data/table/travel_user_booking_fewos', 'TravelUserBookingFewoController@getTravelUserBookingFewos')->name('data_table_travel_user_booking_fewos'); Route::get('/travel_user_booking_fewo/detail/{id}', 'TravelUserBookingFewoController@detail')->name('travel_user_booking_fewo_detail'); - Route::post('/travel_user_booking_fewo/detail/{id}', 'TravelUserBookingFewoController@store')->name('travel_user_booking_fewo_detail'); + Route::post('/travel_user_booking_fewo/detail/{id}', 'TravelUserBookingFewoController@store')->name('travel_user_booking_fewo_detail_store'); Route::get('/travel_user_booking_fewo/delete/{id}/{del?}', 'TravelUserBookingFewoController@delete')->name('travel_user_booking_fewo_delete'); Route::post('/travel_user_booking_fewo/modal/load', 'TravelUserBookingFewoController@loadModal')->name('travel_user_booking_fewo_modal_load'); Route::post('travel_user_booking_fewo/ajax/requests', 'TravelUserBookingFewoController@getAjaxRequests')->name('travel_user_booking_fewo_ajax_requests'); @@ -282,17 +296,17 @@ Route::group(['middleware' => ['admin', '2fa']], function () { //Reisebausteine Route::get('/iq/travel/programms', 'IQ\TravelProgrammController@index')->name('iq_travel_programms'); Route::get('/iq/travel/programm/detail/{id}', 'IQ\TravelProgrammController@detail')->name('iq_travel_programm_detail'); - Route::post('/iq/travel/programm/detail/{id?}', 'IQ\TravelProgrammController@store')->name('iq_travel_programm_detail'); + Route::post('/iq/travel/programm/detail/{id?}', 'IQ\TravelProgrammController@store')->name('iq_travel_programm_detail_store'); Route::get('/iq/travel/programm/delete/{id?}/{del?}', 'IQ\TravelProgrammController@delete')->name('iq_travel_programm_delete'); Route::get('/iq/travel/groups', 'IQ\TravelGroupController@index')->name('iq_travel_groups'); Route::get('/iq/travel/group/detail/{id?}', 'IQ\TravelGroupController@detail')->name('iq_travel_group_detail'); - Route::post('/iq/travel/group/detail/{id?}', 'IQ\TravelGroupController@store')->name('iq_travel_group_detail'); + Route::post('/iq/travel/group/detail/{id?}', 'IQ\TravelGroupController@store')->name('iq_travel_group_detail_store'); Route::get('/iq/travel/group/delete/{id?}/{del?}', 'IQ\TravelGroupController@delete')->name('iq_travel_group_delete'); Route::get('/iq/travel/items', 'IQ\TravelItemController@index')->name('iq_travel_items'); Route::get('/iq/travel/item/detail/{id?}', 'IQ\TravelItemController@detail')->name('iq_travel_item_detail'); - Route::post('/iq/travel/item/detail/{id?}', 'IQ\TravelItemController@store')->name('iq_travel_item_detail'); + Route::post('/iq/travel/item/detail/{id?}', 'IQ\TravelItemController@store')->name('iq_travel_item_detail_store'); Route::get('/iq/travel/item/delete/{id?}/{del?}', 'IQ\TravelItemController@delete')->name('iq_travel_item_delete'); Route::get('/iq/travel/programm/data_table', 'IQ\TravelProgrammController@getTravelProgramms')->name('data_table_travel_programms'); @@ -303,7 +317,7 @@ Route::group(['middleware' => ['admin', '2fa']], function () { Route::group(['middleware' => ['auth.permission:cms-cn-in']], function () { //CMS Infos Route::get('/cms/content/infos', 'CMS\CMSContentInfoController@index')->name('cms_content_infos'); - Route::post('/cms/content/infos', 'CMS\CMSContentInfoController@store')->name('cms_content_infos'); + Route::post('/cms/content/infos', 'CMS\CMSContentInfoController@store')->name('cms_content_infos_store'); Route::get('/cms/content/infos/delete/{model}/{id}', 'CMS\CMSContentInfoController@delete')->name('cms_content_infos_delete'); }); Route::group(['middleware' => ['auth.permission:cms-cn-al']], function () { @@ -324,7 +338,7 @@ Route::group(['middleware' => ['admin', '2fa']], function () { //CMS Fragen & Antworten Route::get('/cms/answer_question', 'CMS\CMSAnswerQuestionController@index')->name('cms_answer_question'); Route::get('/cms/answer_question/detail/{id}', 'CMS\CMSAnswerQuestionController@detail')->name('cms_answer_question_detail'); - Route::post('/cms/answer_question/detail/{id}', 'CMS\CMSAnswerQuestionController@store')->name('cms_answer_question_detail'); + Route::post('/cms/answer_question/detail/{id}', 'CMS\CMSAnswerQuestionController@store')->name('cms_answer_question_detail_store'); Route::get('/cms/answer_question/delete/{id}', 'CMS\CMSAnswerQuestionController@delete')->name('cms_answer_question_delete'); Route::get('/cms/answer_question/datatable', 'CMS\CMSAnswerQuestionController@datatable')->name('cms_answer_question_datatable'); }); @@ -332,20 +346,20 @@ Route::group(['middleware' => ['admin', '2fa']], function () { // CMS Reiseführer Route::get('/cms/travel_guide/content', 'CMS\CMSTravelGuideController@index')->name('cms_travel_guide_content'); Route::get('/cms/travel_guide/page', 'CMS\CMSTravelGuideController@page')->name('cms_travel_guide_page'); - Route::get('/cms/travel_guide/page/detail/{id}', 'CMS\CMSTravelGuideControllecr@pageDetail')->name('cms_travel_guide_page_detail'); + Route::get('/cms/travel_guide/page/detail/{id}', 'CMS\CMSTravelGuideController@pageDetail')->name('cms_travel_guide_page_detail'); Route::get('/cms/travel_guide/test/', 'CMS\CMSTravelGuideController@test')->name('cms_travel_guide_test'); - Route::post('/cms/travel_guide/page/detail/{id}', 'CMS\CMSTravelGuideController@pageStore')->name('cms_travel_guide_page_detail'); + Route::post('/cms/travel_guide/page/detail/{id}', 'CMS\CMSTravelGuideController@pageStore')->name('cms_travel_guide_page_detail_store'); Route::get('/cms/travel_guide/detail/{id}', 'CMS\CMSTravelGuideController@detail')->name('cms_travel_guide_detail'); - Route::post('/cms/travel_guide/detail/{id}', 'CMS\CMSTravelGuideController@store')->name('cms_travel_guide_detail'); + Route::post('/cms/travel_guide/detail/{id}', 'CMS\CMSTravelGuideController@store')->name('cms_travel_guide_detail_store'); Route::get('/cms/travel_guide/delete/{id}', 'CMS\CMSTravelGuideController@delete')->name('cms_travel_guide_delete'); }); Route::group(['middleware' => ['auth.permission:cms-tg']], function () { // CMS FeWo Route::get('/cms/fewo/all/{step?}', 'CMS\CMSFeWoController@all')->name('cms_fewo_all'); - Route::post('/cms/fewo/all/{step?}', 'CMS\CMSFeWoController@storeAll')->name('cms_fewo_all'); + Route::post('/cms/fewo/all/{step?}', 'CMS\CMSFeWoController@storeAll')->name('cms_fewo_all_store'); Route::get('/cms/fewo/all/delete/{id}', 'CMS\CMSFeWoController@deleteAll')->name('cms_fewo_all_delete'); Route::get('/cms/fewo/content', 'CMS\CMSFeWoController@content')->name('cms_fewo_content'); @@ -356,33 +370,33 @@ Route::group(['middleware' => ['admin', '2fa']], function () { // CMS Booking Route::get('/cms/booking/all/', 'CMS\CMSBookingController@all')->name('cms_booking_all'); Route::get('/cms/booking/all/detail/{id?}', 'CMS\CMSBookingController@detailAll')->name('cms_booking_all_detail'); - Route::post('/cms/booking/all/detail/{id?}', 'CMS\CMSBookingController@storeAll')->name('cms_booking_all_detail'); + Route::post('/cms/booking/all/detail/{id?}', 'CMS\CMSBookingController@storeAll')->name('cms_booking_all_detail_store'); Route::get('/cms/booking/all/delete/{id}/{do}', 'CMS\CMSBookingController@deleteAll')->name('cms_booking_all_delete'); Route::get('/cms/booking/content', 'CMS\CMSBookingController@content')->name('cms_booking_content'); Route::get('/cms/booking/content/detail/{id?}', 'CMS\CMSBookingController@detailContent')->name('cms_booking_content_detail'); - Route::post('/cms/booking/content/detail/{id?}', 'CMS\CMSBookingController@storeContent')->name('cms_booking_content_detail'); + Route::post('/cms/booking/content/detail/{id?}', 'CMS\CMSBookingController@storeContent')->name('cms_booking_content_detail_store'); Route::get('/cms/booking/content/delete/{id}/{do}', 'CMS\CMSBookingController@deleteContent')->name('cms_booking_content_delete'); }); Route::group(['middleware' => ['auth.permission:cms-fb']], function () { // CMS Feedback Route::get('/cms/feedback', 'CMS\CMSFeedbackController@index')->name('cms_feedback'); Route::get('/cms/feedback/detail/{id}', 'CMS\CMSFeedbackController@detail')->name('cms_feedback_detail'); - Route::post('/cms/feedback/detail/{id}', 'CMS\CMSFeedbackController@store')->name('cms_feedback_detail'); + Route::post('/cms/feedback/detail/{id}', 'CMS\CMSFeedbackController@store')->name('cms_feedback_detail_store'); Route::get('/cms/feedback/delete/{id}', 'CMS\CMSFeedbackController@delete')->name('cms_feedback_delete'); }); Route::group(['middleware' => ['auth.permission:cms-nw']], function () { // CMS News Route::get('/cms/news', 'CMS\CMSNewsController@index')->name('cms_news'); Route::get('/cms/news/detail/{id}', 'CMS\CMSNewsController@detail')->name('cms_news_detail'); - Route::post('/cms/news/detail/{id}', 'CMS\CMSNewsController@store')->name('cms_news_detail'); + Route::post('/cms/news/detail/{id}', 'CMS\CMSNewsController@store')->name('cms_news_detail_store'); Route::get('/cms/news/delete/{id}', 'CMS\CMSNewsController@delete')->name('cms_news_delete'); }); Route::group(['middleware' => ['auth.permission:cms-sb']], function () { // CMS Sidebar Route::get('/cms/sidebar', 'CMS\CMSSidebarController@index')->name('cms_sidebar'); Route::get('/cms/sidebar/detail/{id}', 'CMS\CMSSidebarController@detail')->name('cms_sidebar_detail'); - Route::post('/cms/sidebar/detail/{id}', 'CMS\CMSSidebarController@store')->name('cms_sidebar_detail'); + Route::post('/cms/sidebar/detail/{id}', 'CMS\CMSSidebarController@store')->name('cms_sidebar_detail_store'); Route::get('/cms/sidebar/delete/{id}', 'CMS\CMSSidebarController@delete')->name('cms_sidebar_delete'); }); Route::group(['middleware' => ['auth.permission:crm-nav-api']], function () { @@ -556,7 +570,7 @@ Route::group(['middleware' => ['superadmin', '2fa']], function () { Route::post('/admin/user/store', 'AdminUserController@store')->name('admin_user_store'); Route::get('/admin/user/change/mail/{user_id}', 'UserUpdateEmailController@adminChangeMail')->name('admin_user_change_mail'); - Route::post('/admin/user/change/mail/{user_id}', 'UserUpdateEmailController@adminUpdateMail')->name('admin_user_change_mail'); + Route::post('/admin/user/change/mail/{user_id}', 'UserUpdateEmailController@adminUpdateMail')->name('admin_user_change_mail_store'); Route::get('/admin/user/delete/{user_id}', 'AdminUserController@deleteUser')->name('admin_user_delete'); Route::get('/admin/users/data_table', 'AdminUserController@getUsers')->name('admin_users_data_table'); diff --git a/tests/Feature/Api/BookingImportTest.php b/tests/Feature/Api/BookingImportTest.php new file mode 100644 index 0000000..164585d --- /dev/null +++ b/tests/Feature/Api/BookingImportTest.php @@ -0,0 +1,45 @@ +postJson('/api/booking/import', [ + 'travel_booking_id' => 1, + ]); + + $response->assertStatus(401); + $response->assertJson(['error' => 'key']); + } + + public function testImportRejectsRequestWithWrongKey(): void + { + $response = $this->postJson('/api/booking/import', [ + 'key' => 'wrong-key', + 'travel_booking_id' => 1, + ]); + + $response->assertStatus(401); + $response->assertJson(['error' => 'key']); + } + + public function testImportReturnsErrorWhenBookingNotFound(): void + { + $response = $this->postJson('/api/booking/import', [ + 'key' => $this->validKey, + 'travel_booking_id' => 999999, + ]); + + $response->assertStatus(200); + $response->assertJson(['error' => 'no-booking-found']); + } +} diff --git a/tests/Feature/Auth/LoginTest.php b/tests/Feature/Auth/LoginTest.php new file mode 100644 index 0000000..3e1e3a1 --- /dev/null +++ b/tests/Feature/Auth/LoginTest.php @@ -0,0 +1,99 @@ +get('/login'); + + $response->assertStatus(200); + } + + public function testUnauthenticatedUserIsRedirectedToLogin(): void + { + $response = $this->get('/home'); + + $response->assertRedirect('/login'); + } + + public function testUserCanLoginWithValidCredentials(): void + { + $user = User::factory()->create([ + 'password' => bcrypt('password'), + ]); + + $response = $this->post('/login', [ + 'email' => $user->email, + 'password' => 'password', + ]); + + $this->assertAuthenticatedAs($user); + $response->assertRedirect(); + } + + public function testLoginFailsWithWrongPassword(): void + { + $user = User::factory()->create([ + 'password' => bcrypt('correct-password'), + ]); + + $response = $this->post('/login', [ + 'email' => $user->email, + 'password' => 'wrong-password', + ]); + + $this->assertGuest(); + $response->assertSessionHasErrors('email'); + } + + public function testLoginFailsWithUnknownEmail(): void + { + $response = $this->post('/login', [ + 'email' => 'nobody@example.com', + 'password' => 'password', + ]); + + $this->assertGuest(); + $response->assertSessionHasErrors('email'); + } + + public function testInactiveUserCannotLogin(): void + { + $user = User::factory()->inactive()->create([ + 'password' => bcrypt('password'), + ]); + + $response = $this->post('/login', [ + 'email' => $user->email, + 'password' => 'password', + ]); + + $this->assertGuest(); + } + + public function testAuthenticatedUserIsRedirectedAwayFromLoginPage(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->get('/login'); + + $response->assertRedirect(); + } + + public function testUserCanLogout(): void + { + $user = User::factory()->create(); + + $this->actingAs($user)->post('/logout'); + + $this->assertGuest(); + } +} diff --git a/tests/Feature/BookingControllerTest.php b/tests/Feature/BookingControllerTest.php new file mode 100644 index 0000000..42f64c8 --- /dev/null +++ b/tests/Feature/BookingControllerTest.php @@ -0,0 +1,53 @@ +get('/booking'); + + $response->assertRedirect('/login'); + } + + public function testGuestIsRedirectedFromBookingDetail(): void + { + $response = $this->get('/booking/1'); + + $response->assertRedirect('/login'); + } + + public function testNonAdminUserCannotAccessBookingIndex(): void + { + $user = User::factory()->create(['admin' => 0]); + + // Middleware 'admin' redirects non-admins to /home + $response = $this->actingAs($user) + ->withoutMiddleware(\App\Http\Middleware\MiddleGoogle2FA::class) + ->get('/booking'); + + $response->assertRedirect('/home'); + } + + public function testAdminUserCanAccessBookingIndexAfterAuthentication(): void + { + $user = User::factory()->admin()->create(); + + $response = $this->actingAs($user) + ->withoutMiddleware([ + \App\Http\Middleware\MiddleGoogle2FA::class, + ]) + ->get('/booking'); + + // Either 200 (page loads) or a redirect due to missing 2FA – in any + // case the admin middleware itself must not block the request. + $response->assertStatus(200); + } +} diff --git a/tests/Unit/Services/UtilTest.php b/tests/Unit/Services/UtilTest.php new file mode 100644 index 0000000..a10ce54 --- /dev/null +++ b/tests/Unit/Services/UtilTest.php @@ -0,0 +1,183 @@ +assertSame('1234,56', Util::_format_number('1.234,56 €')); + $this->assertSame('1234,56', Util::_format_number('EUR 1.234,56')); + $this->assertSame('100', Util::_format_number('100')); + } + + public function testFormatNumberReturnsEmptyStringForNonNumericInput(): void + { + $this->assertSame('', Util::_format_number('abc')); + } + + // ------------------------------------------------------------------------- + // _number_format + // ------------------------------------------------------------------------- + + public function testNumberFormatFormatsWithGermanLocale(): void + { + $this->assertSame('1.234,56', Util::_number_format(1234.56)); + $this->assertSame('0,00', Util::_number_format(0)); + $this->assertSame('1.000,00', Util::_number_format(1000)); + } + + public function testNumberFormatRespectsCustomDecimalPlaces(): void + { + $this->assertSame('1.234,5600', Util::_number_format(1234.56, 4)); + $this->assertSame('1.235', Util::_number_format(1234.56, 0)); + } + + // ------------------------------------------------------------------------- + // _clean_float + // ------------------------------------------------------------------------- + + public function testCleanFloatHandlesGermanCommaFormat(): void + { + $this->assertSame(1234.56, Util::_clean_float('1.234,56')); + $this->assertSame(1234.56, Util::_clean_float('1234,56')); + } + + public function testCleanFloatHandlesDotDecimalFormat(): void + { + $this->assertSame(1234.56, Util::_clean_float('1234.56')); + } + + public function testCleanFloatHandlesCurrencyStrings(): void + { + $this->assertSame(99.9, Util::_clean_float('99,90 €')); + $this->assertSame(0.0, Util::_clean_float('0')); + } + + // ------------------------------------------------------------------------- + // _first_replace + // ------------------------------------------------------------------------- + + public function testFirstReplaceStripsRePrefix(): void + { + $this->assertSame('Betreff', Util::_first_replace('re: Betreff')); + $this->assertSame('Betreff', Util::_first_replace('RE: Betreff')); + $this->assertSame('Betreff', Util::_first_replace('Re: re: Betreff')); + } + + public function testFirstReplaceDoesNotAlterStringWithoutPrefix(): void + { + $this->assertSame('Betreff', Util::_first_replace('Betreff')); + } + + // ------------------------------------------------------------------------- + // _explodeLines / _implodeLines + // ------------------------------------------------------------------------- + + public function testExplodeLinesOnNewlines(): void + { + $result = Util::_explodeLines("Zeile1\nZeile2\nZeile3"); + $this->assertSame(['Zeile1', 'Zeile2', 'Zeile3'], $result); + } + + public function testExplodeLinesOnWindowsNewlines(): void + { + $result = Util::_explodeLines("Zeile1\r\nZeile2"); + $this->assertSame(['Zeile1', 'Zeile2'], $result); + } + + public function testExplodeLinesReturnsFalseForEmptyInput(): void + { + $this->assertNull(Util::_explodeLines(false)); + $this->assertNull(Util::_explodeLines('')); + } + + public function testImplodeLinesJoinsArray(): void + { + $this->assertSame("Zeile1\nZeile2", Util::_implodeLines(['Zeile1', 'Zeile2'], "\n")); + } + + public function testImplodeLinesPassesThroughNonArray(): void + { + $this->assertSame('Zeile1', Util::_implodeLines('Zeile1')); + } + + // ------------------------------------------------------------------------- + // replacePlaceholders + // ------------------------------------------------------------------------- + + public function testReplacePlaceholdersSubstitutesValues(): void + { + $template = 'Hallo {{name}}, deine Buchungs-Nr. ist {{booking_id}}.'; + $result = Util::replacePlaceholders($template, [ + 'name' => 'Max Mustermann', + 'booking_id' => '12345', + ]); + + $this->assertSame('Hallo Max Mustermann, deine Buchungs-Nr. ist 12345.', $result); + } + + public function testReplacePlaceholdersLeavesUnknownPlaceholdersIntact(): void + { + $template = 'Hallo {{name}}, {{unknown}}'; + $result = Util::replacePlaceholders($template, ['name' => 'Max']); + + $this->assertStringContainsString('{{unknown}}', $result); + } + + public function testReplacePlaceholdersHandlesEmptyReplacements(): void + { + $template = 'Kein Platzhalter hier.'; + $this->assertSame($template, Util::replacePlaceholders($template, [])); + } + + // ------------------------------------------------------------------------- + // sanitize + // ------------------------------------------------------------------------- + + public function testSanitizeRemovesSpecialCharsAndLowercases(): void + { + $result = Util::sanitize('Hello World!'); + $this->assertSame('hello_world', $result); + } + + public function testSanitizeRespectsForceUppercase(): void + { + $result = Util::sanitize('Hello', false); + $this->assertSame('Hello', $result); + } + + // ------------------------------------------------------------------------- + // getExtensionFromMime + // ------------------------------------------------------------------------- + + public function testGetExtensionFromMimeReturnsKnownExtensions(): void + { + $this->assertSame('pdf', Util::getExtensionFromMime('application/pdf')); + $this->assertSame('jpg', Util::getExtensionFromMime('image/jpeg')); + $this->assertSame('png', Util::getExtensionFromMime('image/png')); + } + + public function testGetExtensionFromMimeReturnsEmptyStringForUnknown(): void + { + $this->assertSame('', Util::getExtensionFromMime('application/unknown')); + } + + // ------------------------------------------------------------------------- + // _formatBytes + // ------------------------------------------------------------------------- + + public function testFormatBytesFormatsCorrectly(): void + { + $this->assertSame('1 KB', Util::_formatBytes(1024)); + $this->assertSame('1 MB', Util::_formatBytes(1024 * 1024)); + $this->assertSame('0', Util::_formatBytes(0)); + } +}