Compare commits

...

10 commits

Author SHA1 Message Date
Phase-1-Rollback-Agent
e3dc1afd8e WIP: Sicherheitsnetz vor Phase-1-R\u00fcckbau
Enth\u00e4lt gemischt: Laravel-10-Upgrade + Phase 1 (Contacts-Modul, Duplicats-Commands,
Soft-Delete+Merge-Fields) + Phase 2 Code-Umstellungen (inquiry_id, $table='contacts'/'inquiries')
+ Offers-Modul (Migrationen, Models, offer_id in Booking, offer-Disk in filesystems.php).

Phase 2 + Offers werden im folgenden Commit nach dev/backups/phase2-offers-2026-04-17/
verschoben, damit der Workspace auf Phase-1-only (= Test-System-Stand) reduziert ist
und direkt auf Live deploybar wird.

Tarball-Backup zus\u00e4tzlich unter: ../backups-safety/workspace-pre-phase1-rollback-2026-04-17.tar.gz

Made-with: Cursor
2026-04-17 13:40:31 +00:00
389d5d1820 23-01-2026 2026-01-23 17:34:40 +01:00
8fd1f4d451 File Controller, Booking Organisation, import Draft, old arrangements 2025-09-04 10:35:45 +02:00
Kevin Adametz
4eb83def39 Updates to 03-2025 2025-04-01 10:40:14 +02:00
Kevin Adametz
881fc84207 08 2024 2024-08-05 11:58:09 +02:00
Kevin Adametz
c1c613a4b9 last changes since 6-2023 2023-07-03 10:10:09 +02:00
Kevin Adametz
561c5875a7 Fonts, Travel Program 2023-01-25 12:47:23 +01:00
Kevin Adametz
93d1bea8e3 06 2022 2022-06-15 18:05:16 +02:00
Kevin Adametz
34a3d2196b Lead create Booking 2022-04-14 13:22:03 +02:00
Kevin Adametz
3df0e93c2c last 12.21 2021-12-25 02:53:44 +01:00
902 changed files with 149174 additions and 22174 deletions

73
.env
View file

@ -6,38 +6,55 @@ APP_URL=https://mein.sterntours.test
APP_OLD_URL=https://cms-stern-tours.test
#APP_URL_V2=https://v2.stern-tours.de
APP_URL_V2=https://v2-sterntours.test
APP_URL_V2=https://v2.sterntours.test
#APP_URL_STERN=https://www.sterntours.de
APP_URL_STERN=https://sterntours.test
APP_DOMAIN_TLD=test
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=192.168.1.8
DB_PORT=3306
DB_DATABASE=cmssso_db1
DB_USERNAME=kadmin
DB_PASSWORD=KT32vQ7ix
SUCCESS_KEY=f6077389c9ce710e554763a5de02c8ec
DB_CONNECTION_STERN=mysql
DB_HOST_STERN=192.168.1.8
# Standard Database Connection
DB_CONNECTION=mysql
DB_HOST=global-mysql
DB_PORT=3306
DB_DATABASE=stern_crm
DB_USERNAME=root
DB_PASSWORD=password
# STERN Database Connection
DB_HOST_STERN=global-mysql
DB_PORT_STERN=3306
DB_DATABASE_STERN=relaunch1
DB_USERNAME_STERN=kadmin
DB_PASSWORD_STERN=KT32vQ7ix
DB_DATABASE_STERN=stern_db
DB_USERNAME_STERN=root
DB_PASSWORD_STERN=password
# Docker Port Forwards
FORWARD_DB_PORT=33064
FORWARD_DB_PORT_STERN=33065
FORWARD_REDIS_PORT=6379
MYSQL_EXTRA_OPTIONS=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
# Vite Port for Development
VITE_PORT=5173
# Sail Configuration
WWWGROUP=1000
WWWUSER=1000
SAIL_XDEBUG_MODE=off
MAIL_BBC=kevin@adametz.media
MAIL_FEWO_EMPLOYEE=kevin@adametz.media
@ -45,18 +62,32 @@ MAIL_FEWO_EMPLOYEE=kevin@adametz.media
#MAIL_BBC=kontakt@stern-tours.de,thomas.stern@stern-tours.de
MAIL_DRIVER=smtp
MAIL_FROM_NAME="Reisebüro STERN TOURS"
MAIL_FROM_NAME="DEV Reisebüro STERN TOURS"
#MAIL_FROM_ADDRESS=dev@adametz.media
#MAIL_HOST=w017e534.kasserver.com
#MAIL_PORT=587
#MAIL_USERNAME=m0496c96
#MAIL_PASSWORD=mZtVp7WQcs6DC3hf
#MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=stern@sterntours.de
MAIL_HOST=zimbra.managedemail.de
MAIL_PORT=587
MAIL_USERNAME=stern@stern-tours.de
MAIL_PASSWORD=13C!NlecB!Phil4beAxKl
MAIL_ENCRYPTION=TLS
#MAIL_FROM_ADDRESS=stern@sterntours.de
#MAIL_HOST=zimbra.managedemail.de
#MAIL_USERNAME=stern@stern-tours.de
#MAIL_PASSWORD=13C!NlecB!Phil4beAxKl
MAIL_PORT=587
MAIL_ENCRYPTION=TLS
MAIL_FROM_ADDRESS=info@mein.sterntours.de
MAIL_HOST=mail.your-server.de
MAIL_USERNAME=info@mein.sterntours.de
MAIL_PASSWORD=B7f8Ojt98v6tMz8W
#MAIL_FROM_ADDRESS=info@mein.sterntours.de
#MAIL_HOST=mail.your-server.de
#MAIL_USERNAME=info@mein.sterntours.de
#MAIL_PASSWORD=B7f8Ojt98v6tMz8W
#MAIL_FROM_ADDRESS=stern@stern-tours.de
#MAIL_HOST=zimbra.managedemail.de

15
.gitignore vendored
View file

@ -25,11 +25,24 @@ Icon
Network Trash Folder
Temporary Items
.apdisk
.idea/
.claude/
.cursor/
.cursorrules/
.cursorrules.md/
.cursorrules.md.txt/
.cursorrules.md.txt.txt/
.cursorrules.md.txt.txt.txt/
/vendor
/node_modules
/storage/app
/storage/language
/storage/framework
/storage/logs
/public/vendor
.idea
.idea/
.vscode/
_static/
_work/
_storage/

27
.mcp.json Normal file
View file

@ -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"
]
}
}
}

File diff suppressed because it is too large Load diff

181
CLAUDE.md Normal file
View file

@ -0,0 +1,181 @@
<laravel-boost-guidelines>
=== 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()`.
- <code-snippet>public function __construct(public GitHub $github) { }</code-snippet>
- 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.
<code-snippet name="Explicit Return Types and Method Params" lang="php">
protected function isAccessible(User $user, ?string $path = null): bool
{
...
}
</code-snippet>
## 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).
</laravel-boost-guidelines>

455
INIT.md Normal file
View file

@ -0,0 +1,455 @@
# Sterntours Laravel Projekt - Initialisierung & Setup
Diese Dokumentation beschreibt die Initialisierung und Einrichtung des Sterntours Laravel-Projekts.
## 📋 Inhaltsverzeichnis
1. [Voraussetzungen](#voraussetzungen)
2. [Schnellstart](#schnellstart)
3. [Manuelle Installation](#manuelle-installation)
4. [Projekt-Struktur](#projekt-struktur)
5. [Umgebungskonfiguration](#umgebungskonfiguration)
6. [Datenbanken](#datenbanken)
7. [Domains & Routing](#domains--routing)
8. [Häufige Befehle](#häufige-befehle)
9. [Fehlerbehebung](#fehlerbehebung)
---
## Voraussetzungen
Bevor du das Projekt initialisierst, stelle sicher, dass folgende Software installiert ist:
- **Docker Desktop** (Version 20.10+)
- [Download für macOS](https://www.docker.com/products/docker-desktop)
- [Download für Windows](https://www.docker.com/products/docker-desktop)
- [Download für Linux](https://docs.docker.com/engine/install/)
- **Composer** (Version 2.0+)
- [Installation Guide](https://getcomposer.org/download/)
- **Node.js & NPM** (Version 16+ empfohlen)
- [Download](https://nodejs.org/)
- **Git** (für Versionskontrolle)
- [Download](https://git-scm.com/)
---
## Schnellstart
Das Projekt kann mit einem einzigen Befehl initialisiert werden:
```bash
bash init.sh
```
Das Script führt automatisch alle notwendigen Schritte aus:
1. ✓ Prüft Docker-Installation
2. ✓ Erstellt `.env` Datei
3. ✓ Installiert Composer-Dependencies
4. ✓ Installiert NPM-Dependencies
5. ✓ Generiert Application Key
6. ✓ Erstellt Docker-Netzwerke
7. ✓ Startet Docker-Container
8. ✓ Führt Datenbank-Migrationen aus
9. ✓ Erstellt Storage-Links
10. ✓ Optimiert die Application
---
## Manuelle Installation
Falls du das Projekt manuell einrichten möchtest:
### 1. Repository klonen
```bash
git clone <repository-url> sterntours
cd sterntours
```
### 2. Umgebungsvariablen konfigurieren
```bash
cp .env.example .env
```
Bearbeite die `.env` Datei nach Bedarf (siehe [Umgebungskonfiguration](#umgebungskonfiguration)).
### 3. Dependencies installieren
```bash
# Composer Dependencies
composer install
# NPM Dependencies
npm install
```
### 4. Application Key generieren
```bash
php artisan key:generate
```
### 5. Docker Proxy-Netzwerk erstellen
```bash
docker network create proxy
```
### 6. Docker Container starten (Laravel Sail)
```bash
./vendor/bin/sail up -d
```
### 7. Datenbank migrieren
```bash
./vendor/bin/sail artisan migrate
```
### 8. Storage Links erstellen
```bash
./vendor/bin/sail artisan storage:link
```
### 9. Cache optimieren
```bash
./vendor/bin/sail artisan config:cache
./vendor/bin/sail artisan route:cache
./vendor/bin/sail artisan view:cache
```
---
## Projekt-Struktur
```
sterntours/
├── app/ # Application Code
│ ├── Console/ # Artisan Commands
│ ├── Http/ # Controllers, Middleware
│ ├── Models/ # Eloquent Models
│ ├── Services/ # Business Logic
│ └── Repositories/ # Data Access Layer
├── config/ # Konfigurationsdateien
├── database/ # Migrations, Seeds, Factories
├── public/ # Öffentliche Assets
├── resources/ # Views, Assets (Sass, JS)
├── routes/ # Route Definitionen
├── storage/ # Logs, Cache, Uploads
├── tests/ # Unit & Feature Tests
├── docker-compose.yml # Docker Setup
├── init.sh # Initialisierungsskript
└── INIT.md # Diese Datei
```
---
## Umgebungskonfiguration
### Wichtige .env Variablen
#### Haupt-Datenbank (CRM)
```env
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=stern_crm
DB_USERNAME=sail
DB_PASSWORD=password
```
#### Stern-Datenbank (Legacy)
```env
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-Konfiguration (Mailpit)
```env
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_ENCRYPTION=null
```
#### Redis Cache
```env
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
```
---
## Datenbanken
Das Projekt verwendet **zwei separate MySQL-Datenbanken**:
### 1. stern_crm (Haupt-CRM)
- **Port:** 33064
- **Verwendung:** Haupt-Application, Kundenverwaltung
- **Host in Container:** mysql
### 2. stern_db (Legacy Datenbank)
- **Port:** 33065
- **Verwendung:** Alte Stern-Tours Daten
- **Host in Container:** mysql-stern
### Datenbankzugriff von außen
```bash
# CRM Datenbank
mysql -h 127.0.0.1 -P 33064 -u sail -p stern_crm
# Stern Datenbank
mysql -h 127.0.0.1 -P 33065 -u sail -p stern_db
```
---
## Domains & Routing
Das Projekt verwendet **Traefik** als Reverse Proxy und ist über folgende Domains erreichbar:
| Domain | Zweck | Port |
|--------|-------|------|
| `https://mein.sterntours.test` | Haupt-Application | 443 |
| `https://sterntours.test` | Alternative URL | 443 |
| `https://assets.sterntours.test` | Vite Dev-Server | 5173 |
| `https://sterntours-mail.test` | Mailpit Dashboard | 8025 |
### Hosts-Datei konfigurieren
Füge folgende Einträge zu deiner `/etc/hosts` (Linux/Mac) oder `C:\Windows\System32\drivers\etc\hosts` (Windows) hinzu:
```
127.0.0.1 mein.sterntours.test
127.0.0.1 sterntours.test
127.0.0.1 assets.sterntours.test
127.0.0.1 sterntours-mail.test
```
---
## Häufige Befehle
### Docker Container
```bash
# Container starten
./vendor/bin/sail up -d
# Container stoppen
./vendor/bin/sail down
# Container neu bauen
./vendor/bin/sail build --no-cache
# Logs anzeigen
./vendor/bin/sail logs -f
# Spezifischen Service anzeigen
./vendor/bin/sail logs mysql -f
```
### Artisan Befehle
```bash
# Migrationen ausführen
./vendor/bin/sail artisan migrate
# Migrationen zurücksetzen
./vendor/bin/sail artisan migrate:rollback
# Seeds ausführen
./vendor/bin/sail artisan db:seed
# Cache leeren
./vendor/bin/sail artisan cache:clear
./vendor/bin/sail artisan config:clear
./vendor/bin/sail artisan route:clear
./vendor/bin/sail artisan view:clear
# Cache optimieren
./vendor/bin/sail artisan config:cache
./vendor/bin/sail artisan route:cache
./vendor/bin/sail artisan view:cache
```
### Composer
```bash
# Packages installieren
./vendor/bin/sail composer install
# Package hinzufügen
./vendor/bin/sail composer require <package>
# Package entfernen
./vendor/bin/sail composer remove <package>
# Autoload aktualisieren
./vendor/bin/sail composer dump-autoload
```
### NPM / Assets
```bash
# Dependencies installieren
npm install
# Development Build
npm run dev
# Production Build
npm run prod
# Watch Mode
npm run watch
```
### Tests
```bash
# Alle Tests ausführen
./vendor/bin/sail test
# Spezifische Test-Datei
./vendor/bin/sail test tests/Feature/ExampleTest.php
# Mit Coverage
./vendor/bin/sail test --coverage
```
### Shell / SSH
```bash
# Shell im Container öffnen
./vendor/bin/sail shell
# Root Shell
./vendor/bin/sail root-shell
# MySQL Shell
./vendor/bin/sail mysql
# Redis CLI
./vendor/bin/sail redis
```
---
## Fehlerbehebung
### Problem: "Docker is not running"
**Lösung:** Starte Docker Desktop und warte, bis es vollständig gestartet ist.
```bash
# macOS/Linux
sudo systemctl start docker
# Windows
Starte Docker Desktop über das Startmenü
```
### Problem: "Port already in use"
**Lösung:** Prüfe, welche Ports belegt sind und ändere sie in der `.env` Datei:
```bash
# Ports prüfen
lsof -i :33064
lsof -i :33065
# In .env ändern
FORWARD_DB_PORT=33064
FORWARD_DB_PORT_STERN=33065
```
### Problem: "Network proxy not found"
**Lösung:** Erstelle das Traefik Proxy-Netzwerk manuell:
```bash
docker network create proxy
```
### Problem: "Permission denied" beim init.sh
**Lösung:** Mache das Script ausführbar:
```bash
chmod +x init.sh
./init.sh
```
### Problem: "Class not found"
**Lösung:** Regeneriere Composer Autoload:
```bash
./vendor/bin/sail composer dump-autoload
./vendor/bin/sail artisan clear-compiled
```
### Problem: "Migration failed"
**Lösung:** Prüfe Datenbankverbindung und setze zurück:
```bash
# Verbindung testen
./vendor/bin/sail artisan tinker
>>> DB::connection()->getPdo();
# Migrationen zurücksetzen
./vendor/bin/sail artisan migrate:fresh
```
### Problem: "Vite/Assets nicht geladen"
**Lösung:** Starte den Dev-Server:
```bash
npm run dev
# oder
npm run watch
```
---
## Support & Kontakt
Bei Fragen oder Problemen:
1. Prüfe die [Fehlerbehebung](#fehlerbehebung)
2. Prüfe die [Laravel Dokumentation](https://laravel.com/docs)
3. Prüfe die [Laravel Sail Dokumentation](https://laravel.com/docs/sail)
---
## Changelog
### Version 1.0.0 (2025-11-07)
- ✓ Initiales Setup-Script erstellt
- ✓ Dokumentation erstellt
- ✓ Docker Compose Konfiguration
- ✓ Dual-Datenbank Setup
- ✓ Traefik Routing konfiguriert
---
**Viel Erfolg mit dem Projekt! 🚀**

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
<?php
namespace App\Console\Commands;
use App\Models\NewsletterContact;
use Illuminate\Console\Command;
class CleanupNewsletterBlockedEmails extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'newsletter:cleanup-blocked-emails {--dry-run : Nur anzeigen, ohne zu löschen}';
/**
* The console description of the command.
*
* @var string
*/
protected $description = 'Entfernt Newsletter-Kontakte mit blockierten E-Mail-Adressen (Alias/Proxy von Buchungsplattformen)';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->info('Suche nach blockierten E-Mail-Adressen...');
$dryRun = $this->option('dry-run');
// Liste der blockierten Domains
$blockedDomains = [
'@guest.booking.com',
'@messages.homeaway.com',
'@fewo.check24.de',
'@booking.com',
'@homeaway.com',
'@check24.de',
'@partner.booking.com',
];
$stats = [
'found' => 0,
'deleted' => 0,
];
// Hole alle Newsletter-Kontakte
$contacts = NewsletterContact::all();
$this->info("Prüfe {$contacts->count()} Kontakte...");
$bar = $this->output->createProgressBar($contacts->count());
$bar->start();
foreach ($contacts as $contact) {
$emailLower = strtolower($contact->email);
$isBlockedEmail = false;
foreach ($blockedDomains as $domain) {
if (str_ends_with($emailLower, strtolower($domain))) {
$isBlockedEmail = true;
break;
}
}
if ($isBlockedEmail) {
$stats['found']++;
if ($dryRun) {
$this->newLine();
$this->warn("Würde löschen: {$contact->email} (ID: {$contact->id})");
} else {
// Log erstellen vor dem Löschen
$contact->logs()->create([
'action' => 'deleted',
'description' => 'Kontakt entfernt - blockierte E-Mail-Domain (Buchungsplattform-Alias)',
]);
$contact->delete();
$stats['deleted']++;
}
}
$bar->advance();
}
$bar->finish();
$this->newLine(2);
// Statistiken ausgeben
if ($dryRun) {
$this->info('Dry-Run abgeschlossen!');
$this->table(
['Statistik', 'Anzahl'],
[
['Gefundene blockierte E-Mails', $stats['found']],
]
);
if ($stats['found'] > 0) {
$this->newLine();
$this->info('Führe den Befehl ohne --dry-run aus, um die Kontakte zu löschen:');
$this->comment('php artisan newsletter:cleanup-blocked-emails');
}
} else {
$this->info('Bereinigung abgeschlossen!');
$this->table(
['Statistik', 'Anzahl'],
[
['Gefundene blockierte E-Mails', $stats['found']],
['Gelöschte Kontakte', $stats['deleted']],
]
);
}
return 0;
}
}

View file

@ -0,0 +1,160 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
/**
* Phase 1 Schritt 1: Duplikate identifizieren
*
* Sucht Kunden-Datensätze, die vermutlich dieselbe Person repräsentieren.
* Drei Erkennungs-Stufen (absteigend nach Konfidenz):
*
* HIGH gleiche E-Mail (nicht leer)
* MEDIUM gleicher Name + Vorname + Geburtsdatum
* LOW gleicher Name + Vorname + PLZ
*
* Verwendung:
* php artisan contacts:find-duplicates
* php artisan contacts:find-duplicates --export=duplicates.csv
* php artisan contacts:find-duplicates --confidence=HIGH
*/
class ContactsFindDuplicates extends Command
{
protected $signature = 'contacts:find-duplicates
{--export= : Pfad zur CSV-Ausgabedatei}
{--confidence= : Nur diese Konfidenz-Stufe ausgeben (HIGH|MEDIUM|LOW)}';
protected $description = 'Identifiziert doppelte Customer-Datensätze anhand von E-Mail, Name/Geburtsdatum oder Name/PLZ';
public function handle(): int
{
$this->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('<fg=green>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('<fg=yellow>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('<fg=red>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);
}
}

View file

@ -0,0 +1,233 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
/**
* Phase 1 Schritt 2: Duplikate zusammenführen
*
* Strategie: Der neueste Datensatz (höchstes updated_at, dann höchste id)
* wird Master. Alle anderen Datensätze derselben Gruppe erhalten
* merged_into_id = master_id und werden nicht mehr zurückgegeben.
*
* Alle FK-Referenzen in lead, booking, customer_mails, lead_mails
* werden auf den Master umgestellt.
*
* Verwendung:
* php artisan contacts:merge-duplicates --dry-run # Vorschau, keine Änderung
* php artisan contacts:merge-duplicates --confidence=HIGH # Nur sichere Duplikate
* php artisan contacts:merge-duplicates # Ausführen
*/
class ContactsMergeDuplicates extends Command
{
protected $signature = 'contacts:merge-duplicates
{--dry-run : Zeigt was passieren würde, ohne Daten zu ändern}
{--confidence= : Nur diese Konfidenz-Stufe verarbeiten (HIGH|MEDIUM|LOW)}
{--force : Überspringt Sicherheitsabfrage}';
protected $description = 'Führt doppelte Customer-Datensätze zusammen (neuester wird Master)';
private bool $dryRun = false;
private int $mergedCount = 0;
private int $updatedLeads = 0;
private int $updatedBookings = 0;
public function handle(): int
{
$this->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++;
}
}

View file

@ -0,0 +1,254 @@
<?php
namespace App\Console\Commands;
use App\Models\TravelUserBookingFewo;
use App\Models\TravelUser;
use App\Models\NewsletterContact;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class SyncNewsletterFerienwohnungen extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'newsletter:sync-ferienwohnungen {--force : Force full sync}';
/**
* The console description of the command.
*
* @var string
*/
protected $description = 'Synchronisiert Ferienwohnungs-Buchungen mit Newsletter-Kontakten';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->info('Starte Synchronisation von Ferienwohnungs-Buchungen...');
$force = $this->option('force');
// Statistiken
$stats = [
'processed' => 0,
'created' => 0,
'updated' => 0,
'skipped' => 0,
'errors' => 0,
];
// Hole alle Buchungen mit TravelUser und invoice_number
$query = TravelUserBookingFewo::with(['travel_user'])
->whereNotNull('travel_user_id')
->whereNotNull('invoice_number')
->where('invoice_number', '!=', '')
// Nur wenn invoice_number eine reine Nummer ist (keine Storno etc.)
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
->whereHas('travel_user', function ($q) {
$q->whereNotNull('email')
->where('email', '!=', '');
})
// Nur Buchungen, bei denen die Reise bereits beendet ist (to_date in der Vergangenheit)
->whereNotNull('to_date')
->where('to_date', '<', now());
if (!$force) {
// Nur Buchungen der letzten 30 Tage (basierend auf Rückreisedatum) wenn nicht --force
$query->where('to_date', '>=', now()->subDays(30));
}
$bookings = $query->get();
$this->info("Verarbeite {$bookings->count()} Buchungen...");
$bar = $this->output->createProgressBar($bookings->count());
$bar->start();
foreach ($bookings as $booking) {
try {
$stats['processed']++;
$travelUser = $booking->travel_user;
// Validiere E-Mail
if (!$travelUser || !$travelUser->email || !filter_var($travelUser->email, FILTER_VALIDATE_EMAIL)) {
$stats['skipped']++;
$bar->advance();
continue;
}
// Filtere Alias/Proxy E-Mail-Adressen von Buchungsplattformen
$blockedDomains = [
'@guest.booking.com',
'@messages.homeaway.com',
'@fewo.check24.de',
'@booking.com',
'@homeaway.com',
'@check24.de',
'@partner.booking.com',
];
$emailLower = strtolower($travelUser->email);
$isBlockedEmail = false;
foreach ($blockedDomains as $domain) {
if (str_ends_with($emailLower, strtolower($domain))) {
$isBlockedEmail = true;
break;
}
}
if ($isBlockedEmail) {
$stats['skipped']++;
$bar->advance();
continue;
}
// Prüfe ob invoice_number wirklich eine reine Zahl ist
if (!preg_match('/^[0-9]+$/', $booking->invoice_number)) {
$stats['skipped']++;
$bar->advance();
continue;
}
// Generiere Hash für Duplikat-Erkennung
$syncHash = NewsletterContact::generateSyncHash(
$travelUser->email,
NewsletterContact::SOURCE_BOOKING_FERIENWOHNUNGEN
);
// Suche oder erstelle Kontakt
$contact = NewsletterContact::withTrashed()
->where('email', strtolower(trim($travelUser->email)))
->first();
$isNew = false;
if (!$contact) {
// Neuer Kontakt
$contact = new NewsletterContact();
$isNew = true;
$stats['created']++;
} else {
// Wenn gelöscht, wiederherstellen
if ($contact->trashed()) {
$contact->restore();
}
$stats['updated']++;
}
// Aktualisiere Kontaktdaten
$contact->email = strtolower(trim($travelUser->email));
$contact->firstname = $travelUser->first_name ?: $contact->firstname;
$contact->lastname = $travelUser->last_name ?: $contact->lastname;
// Setze Gruppe Ferienwohnungen
$contact->group_ferienwohnungen = true;
// Source nur bei neuem Kontakt setzen (wenn noch nicht aus Kulturreisen)
if ($isNew) {
$contact->source = NewsletterContact::SOURCE_BOOKING_FERIENWOHNUNGEN;
$contact->subscribed_at = $booking->booking_date ?
\Carbon\Carbon::parse($booking->booking_date) :
$booking->created_at;
}
// Referenz zum TravelUser
$contact->travel_user_id = $travelUser->id;
// Aktualisiere Buchungsstatistiken
// Nur Buchungen mit invoice_number (reine Nummer) zählen
$userBookings = TravelUserBookingFewo::where('travel_user_id', $travelUser->id)
->whereNotNull('invoice_number')
->where('invoice_number', '!=', '')
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
->count();
$contact->total_bookings_ferienwohnungen = $userBookings;
// Letztes Buchungsdatum
$lastBooking = TravelUserBookingFewo::where('travel_user_id', $travelUser->id)
->whereNotNull('invoice_number')
->where('invoice_number', '!=', '')
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
->orderBy('booking_date', 'DESC')
->first();
if ($lastBooking && $lastBooking->booking_date) {
$lastBookingDate = \Carbon\Carbon::parse($lastBooking->booking_date);
if (!$contact->last_booking_at || $lastBookingDate->gt($contact->last_booking_at)) {
$contact->last_booking_at = $lastBookingDate;
}
}
// Letztes Reiseenddatum (to_date) - nur abgeschlossene Reisen
$lastTravelEndBooking = TravelUserBookingFewo::where('travel_user_id', $travelUser->id)
->whereNotNull('invoice_number')
->where('invoice_number', '!=', '')
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
->whereNotNull('to_date')
->where('to_date', '<', now())
->orderBy('to_date', 'DESC')
->first();
if ($lastTravelEndBooking && $lastTravelEndBooking->to_date) {
$lastTravelEndDate = \Carbon\Carbon::parse($lastTravelEndBooking->to_date);
if (!$contact->last_travel_end_date || $lastTravelEndDate->gt($contact->last_travel_end_date)) {
$contact->last_travel_end_date = $lastTravelEndDate;
}
}
// Status
if ($isNew || $contact->status === NewsletterContact::STATUS_INACTIVE) {
$contact->status = NewsletterContact::STATUS_ACTIVE;
}
$contact->sync_hash = $syncHash;
$contact->last_synced_at = now();
$contact->save();
// Log erstellen
if ($isNew) {
$contact->logs()->create([
'action' => 'booking_added',
'description' => 'Kontakt durch Ferienwohnungs-Buchung erstellt',
'metadata' => [
'booking_id' => $booking->id,
'invoice_number' => $booking->invoice_number,
],
]);
}
} catch (\Exception $e) {
$stats['errors']++;
$this->error("Fehler bei Buchung {$booking->id}: " . $e->getMessage());
}
$bar->advance();
}
$bar->finish();
$this->newLine(2);
// Statistiken ausgeben
$this->info('Synchronisation abgeschlossen!');
$this->table(
['Statistik', 'Anzahl'],
[
['Verarbeitet', $stats['processed']],
['Neu erstellt', $stats['created']],
['Aktualisiert', $stats['updated']],
['Übersprungen', $stats['skipped']],
['Fehler', $stats['errors']],
]
);
return 0;
}
}

View file

@ -0,0 +1,227 @@
<?php
namespace App\Console\Commands;
use App\Models\Booking;
use App\Models\Customer;
use App\Models\NewsletterContact;
use App\Models\Status;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class SyncNewsletterKulturreisen extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'newsletter:sync-kulturreisen {--force : Force full sync}';
/**
* The console description of the command.
*
* @var string
*/
protected $description = 'Synchronisiert Kulturreisen-Buchungen mit Newsletter-Kontakten';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->info('Starte Synchronisation von Kulturreisen-Buchungen...');
$force = $this->option('force');
// Statistiken
$stats = [
'processed' => 0,
'created' => 0,
'updated' => 0,
'skipped' => 0,
'errors' => 0,
];
// Hole alle Buchungen mit Kunden
$query = Booking::with(['customer', 'lead.status'])
->whereNotNull('customer_id')
->whereHas('customer', function ($q) {
$q->whereNotNull('email')
->where('email', '!=', '');
})
// Nur Buchungen, bei denen die Reise bereits beendet ist (end_date in der Vergangenheit)
->whereNotNull('end_date')
->where('end_date', '<', now());
if (!$force) {
// Nur Buchungen der letzten 30 Tage (basierend auf Rückreisedatum) wenn nicht --force
$query->where('end_date', '>=', now()->subDays(30));
}
$bookings = $query->get();
$this->info("Verarbeite {$bookings->count()} Buchungen...");
$bar = $this->output->createProgressBar($bookings->count());
$bar->start();
foreach ($bookings as $booking) {
try {
$stats['processed']++;
$customer = $booking->customer;
// Validiere E-Mail
if (!$customer || !$customer->email || !filter_var($customer->email, FILTER_VALIDATE_EMAIL)) {
$stats['skipped']++;
$bar->advance();
continue;
}
// Filtere Alias/Proxy E-Mail-Adressen von Buchungsplattformen
$blockedDomains = [
'@guest.booking.com',
'@messages.homeaway.com',
'@fewo.check24.de',
'@booking.com',
'@homeaway.com',
'@check24.de',
'@partner.booking.com',
];
$emailLower = strtolower($customer->email);
$isBlockedEmail = false;
foreach ($blockedDomains as $domain) {
if (str_ends_with($emailLower, strtolower($domain))) {
$isBlockedEmail = true;
break;
}
}
if ($isBlockedEmail) {
$stats['skipped']++;
$bar->advance();
continue;
}
// Generiere Hash für Duplikat-Erkennung
$syncHash = NewsletterContact::generateSyncHash(
$customer->email,
NewsletterContact::SOURCE_BOOKING_KULTURREISEN
);
// Suche oder erstelle Kontakt
$contact = NewsletterContact::withTrashed()
->where('email', strtolower(trim($customer->email)))
->first();
$isNew = false;
if (!$contact) {
// Neuer Kontakt
$contact = new NewsletterContact();
$isNew = true;
$stats['created']++;
} else {
// Wenn gelöscht, wiederherstellen
if ($contact->trashed()) {
$contact->restore();
}
$stats['updated']++;
}
// Aktualisiere Kontaktdaten
$contact->email = strtolower(trim($customer->email));
$contact->firstname = $customer->firstname ?: $contact->firstname;
$contact->lastname = $customer->name ?: $contact->lastname;
// Setze Gruppe Kulturreisen
$contact->group_kulturreisen = true;
// Source nur bei neuem Kontakt setzen
if ($isNew) {
$contact->source = NewsletterContact::SOURCE_BOOKING_KULTURREISEN;
$contact->subscribed_at = $booking->booking_date ?: $booking->created_at;
}
// Referenz zum Customer
$contact->customer_id = $customer->id;
// Aktualisiere Buchungsstatistiken
$customerBookings = Booking::where('customer_id', $customer->id)->count();
$contact->total_bookings_kulturreisen = $customerBookings;
// Letztes Buchungsdatum
$lastBooking = Booking::where('customer_id', $customer->id)
->orderBy('booking_date', 'DESC')
->first();
if ($lastBooking && $lastBooking->booking_date) {
$contact->last_booking_at = $lastBooking->booking_date;
}
// Letztes Reiseenddatum (end_date) - nur abgeschlossene Reisen
$lastTravelEndBooking = Booking::where('customer_id', $customer->id)
->whereNotNull('end_date')
->where('end_date', '<', now())
->orderBy('end_date', 'DESC')
->first();
if ($lastTravelEndBooking && $lastTravelEndBooking->end_date) {
if (!$contact->last_travel_end_date || $lastTravelEndBooking->end_date->gt($contact->last_travel_end_date)) {
$contact->last_travel_end_date = $lastTravelEndBooking->end_date;
}
}
// Status
if ($isNew || $contact->status === NewsletterContact::STATUS_INACTIVE) {
$contact->status = NewsletterContact::STATUS_ACTIVE;
}
$contact->sync_hash = $syncHash;
$contact->last_synced_at = now();
$contact->save();
// Log erstellen
if ($isNew) {
$contact->logs()->create([
'action' => 'booking_added',
'description' => 'Kontakt durch Kulturreisen-Buchung erstellt',
'metadata' => [
'booking_id' => $booking->id,
// Metadaten-Key bleibt `lead_id`; Wert kommt aus booking.inquiry_id.
'lead_id' => $booking->inquiry_id,
],
]);
}
} catch (\Exception $e) {
$stats['errors']++;
$this->error("Fehler bei Buchung {$booking->id}: " . $e->getMessage());
}
$bar->advance();
}
$bar->finish();
$this->newLine(2);
// Statistiken ausgeben
$this->info('Synchronisation abgeschlossen!');
$this->table(
['Statistik', 'Anzahl'],
[
['Verarbeitet', $stats['processed']],
['Neu erstellt', $stats['created']],
['Aktualisiert', $stats['updated']],
['Übersprungen', $stats['skipped']],
['Fehler', $stats['errors']],
]
);
return 0;
}
}

View file

@ -0,0 +1,18 @@
Newsletter-Synchronisation (wie gewohnt):
# Normale Synchronisation (letzte 30 Tage)
php artisan newsletter:sync-ferienwohnungenphp artisan newsletter:sync-kulturreisen
# Vollständige Synchronisation
php artisan newsletter:sync-ferienwohnungen --forcephp artisan newsletter:sync-kulturreisen --force
#Bereinigung bestehender blockierter E-Mails:
# Erst testen (zeigt nur an, was gelöscht würde)
php artisan newsletter:cleanup-blocked-emails --dry-run
# Tatsächlich löschen
php artisan newsletter:cleanup-blocked-emails

View file

@ -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');
}

View file

@ -0,0 +1,76 @@
<?php
namespace App\Exports;
use App\Models\NewsletterContact;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
class NewsletterExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize
{
protected $contacts;
public function __construct($contacts)
{
$this->contacts = $contacts;
}
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return $this->contacts;
}
/**
* @return array
*/
public function headings(): array
{
return [
'ID',
'E-Mail',
'Vorname',
'Nachname',
'Gruppe Kulturreisen',
'Gruppe Ferienwohnungen',
'Status',
'Herkunft',
'Buchungen Kulturreisen',
'Buchungen Ferienwohnungen',
'Letzte Buchung',
'Letzte Reise',
'Angemeldet am',
'Abgemeldet am',
'Erstellt am',
];
}
/**
* @param NewsletterContact $contact
* @return array
*/
public function map($contact): array
{
return [
$contact->id,
$contact->email,
$contact->firstname,
$contact->lastname,
$contact->group_kulturreisen ? 'Ja' : 'Nein',
$contact->group_ferienwohnungen ? 'Ja' : 'Nein',
$contact->status_label,
$contact->source_label,
$contact->total_bookings_kulturreisen,
$contact->total_bookings_ferienwohnungen,
$contact->last_booking_at ? $contact->last_booking_at->format('d.m.Y') : '',
$contact->last_travel_end_date ? $contact->last_travel_end_date->format('d.m.Y') : '',
$contact->subscribed_at ? $contact->subscribed_at->format('d.m.Y H:i') : '',
$contact->unsubscribed_at ? $contact->unsubscribed_at->format('d.m.Y H:i') : '',
$contact->created_at->format('d.m.Y H:i'),
];
}
}

View file

@ -0,0 +1,313 @@
<?php
namespace App\Helper;
use App\Models\Booking;
use App\Models\BookingDocument as BookingDocumentModel;
class BookingDocument
{
private static $files_count = 0;
public static function showButton($identifier, Booking $booking){
switch($identifier){
case 'registration':
return $booking->price > 0 ? true : false;
break;
case 'confirmation':
return $booking->hasDocument('registration') > 0 ? true : false;
break;
case 'storno':
return $booking->hasDocument('confirmation') > 0 ? true : false;
break;
case 'coupon':
return $booking->hasDocument('confirmation') > 0 ? true : false;
case 'voucher':
return $booking->hasDocument('confirmation') > 0 ? true : false;
case 'voucher_agency':
return $booking->hasDocument('confirmation') > 0 ? true : false;
}
return false;
}
/*
functions to make the html table for the booking documents
*/
public static function getFilesCount(){
return self::$files_count++;
}
public static function getBookingDocumentsHTMLTable($identifier, Booking $booking, $look = 'show'){
$ret = "";
$files = self::getBookingDocuments($identifier, $booking->id);
if($files && $files->count() > 0){
$ret .= self::makeHTMLTable($files, $identifier, 'v3', $look);
}
$files = self::getV2BookingDocuments($identifier, $booking);
if($files && $files->count() > 0){
$ret .= self::makeHTMLTable($files, $identifier, 'v2', $look);
}
return $ret;
}
public static function getBookingDocuments($identifier, $booking_id){
switch($identifier){
case 'registration':
$files = BookingDocumentModel::where('booking_id', $booking_id)->where('identifier', 'registration')->get();
break;
case 'confirmation':
$files = BookingDocumentModel::where('booking_id', $booking_id)->where('identifier', 'confirmation')->get();
break;
case 'storno':
$files = null; //BookingDocumentModel::where('booking_id', $booking_id)->where('identifier', 'storno')->get();
break;
case 'coupon':
$files = null; //BookingDocumentModel::where('booking_id', $booking_id)->where('identifier', 'coupon')->get();
break;
case 'voucher':
$files = BookingDocumentModel::where('booking_id', $booking_id)->where('identifier', 'voucher')->get();
break;
case 'voucher_agency':
$files = BookingDocumentModel::where('booking_id', $booking_id)->where('identifier', 'voucher_agency')->get();
break;
default:
$files = null;
}
return $files;
}
public static function getV2BookingDocuments($identifier, Booking $booking){
switch($identifier){
case 'registration':
$files = $booking->booking_applications;
break;
case 'confirmation':
$files = $booking->booking_confirmations;
break;
case 'storno':
$files = $booking->booking_stornos;
break;
case 'coupon':
$files = $booking->coupons;
break;
case 'voucher':
$files = $booking->booking_vouchers;
break;
case 'voucher_agency':
$files = $booking->booking_voucher_agencys;
break;
default:
$files = null;
}
return $files;
}
private static function makeHTMLTable($files, $identifier, $version, $look){
$ret = "";
foreach($files as $file){
;
if($version === 'v2'){
$file_details = self::getV2FileDetails($file, $identifier, $look);
}
if($version === 'v3'){
$file_details = self::getV3FileDetails($file, $identifier, $look);
}
if(isset($file_details)){
$ret .= "<tr>";
$ret .= "<th scope='row'>".self::getFilesCount()."</th>";
$ret .= "<td>";
$ret .= "<a href='".$file_details->route."' target='_blank' class='badge badge-md badge-".$file_details->color."'>";
$ret .= "<i class='fa fa-file-pdf mr-1 ".(($file_details->opacity) ? 'opacity-50' : '')."'></i> ".$file_details->name;
$ret .= "</a>";
$ret .= "</td>";
$ret .= "<td>".$file_details->cell."</td>";
$ret .= "<td>".$file_details->date."</td>";
$ret .= "<td>";
if($look === 'show'){
$ret .= "<a href='".$file_details->donwload."' class='btn btn-xs btn-default' title='Download' data-placement='left' rel='tooltip'>";
$ret .= "<i class='fa fa-download'></i>";
$ret .= "</a>";
}
if($look === 'mail'){
$ret .= "<button data-target='".$file_details->route."' data-name='".$file_details->name.".pdf' class='btn btn-xs btn-primary add-file-to-attachment'";
$ret .= " title='als Anhang hinzufügen' data-placement='left' rel='tooltip'>";
$ret .= "<i class='fa fa-cloud-download-alt'></i>";
$ret .= "</button>";
}
$ret .= "</td>";
$ret .= "</tr>";
}
}
return $ret;
}
private static function getV3FileDetails($file, $identifier, $look){
$ret = new \stdClass();
switch($identifier){
case 'registration':
$ret->name = "Buchnungsauftrag ".$file->data->number;
$ret->color = "primary";
$ret->cell = self::getCellLabel($look, 'GP').": ".\App\Services\Util::_number_format((float) $file->data->total)." &euro;";
$ret->date = \App\Services\Util::_format_date($file->data->date, 'date');
break;
case 'confirmation':
$ret->name = "Reisebestätigung ".$file->data->number;
$ret->color = "success";
$ret->cell = self::getCellLabel($look, 'GP').": ".\App\Services\Util::_number_format($file->data->total)." &euro; <br>
".self::getCellLabel($look, 'AZ').": ".\App\Services\Util::_number_format($file->data->deposit)." &euro; <br>
".self::getCellLabel($look, 'RZ').": ".\App\Services\Util::_number_format($file->data->final_payment)." &euro;";
$ret->date = \App\Services\Util::_format_date($file->data->date, 'date');
break;
case 'storno':
$ret->name = "Stornobestätigung ".$file->data->number;
$ret->color = "danger";
$ret->cell = self::getCellLabel($look, 'SB').": ".\App\Services\Util::_number_format($file->total)." &euro;";
$ret->date = \App\Services\Util::_format_date($file->data->date, 'date');
break;
case 'coupon':
$ret->name = "Gutschein ".$file->number;
$ret->color = "warning";
$ret->cell = self::getCellLabel($look, 'GW').": ".\App\Services\Util::_number_format($file->value)." &euro; |
bis: ".\App\Services\Util::_format_date($file->valid_date, 'date')." |
".(($file->is_redeemed) ? '<i class="fa fa-check-circle text-success"></i> '.\App\Services\Util::_format_date($file->redeem_date, 'date') : '<i class="fa fa-times-circle text-danger"></i>')."";
$ret->date = \App\Services\Util::_format_date($file->issue_date, 'date');
break;
case 'voucher':
$ret->name = " Voucher ".$file->data->number;
$ret->color = "dark";
$ret->cell = self::getCellLabel($look, 'VC');
$ret->date = \App\Services\Util::_format_date($file->data->date, 'date');
break;
case 'voucher_agency':
$ret->name = "Voucher-Agentur ".$file->data->number;
$ret->color = "dark";
$ret->cell = self::getCellLabel($look, 'VA');
$ret->date = \App\Services\Util::_format_date($file->data->date, 'date');
break;
default:
$ret->name = "";
$ret->color = "";
$ret->cell = "";
$ret->date = "";
}
$ret->route = $file->getURL('file');
$ret->donwload = $file->getURL('download');
$ret->opacity = false;
return $ret;
}
private static function getV2FileDetails($file, $identifier, $look){
$ret = new \stdClass();
switch($identifier){
case 'registration':
$ret->name = "Reiseanmeldung";
$ret->color = "primary";
$ret->cell = self::getCellLabel($look, 'GP').": ".\App\Services\Util::_number_format($file->total)." &euro;";
$ret->date = \App\Services\Util::_format_date($file->updated_at, 'date');
break;
case 'confirmation':
$ret->name = "Reisebestätigung";
$ret->color = "success";
$ret->cell = self::getCellLabel($look, 'GP').": ".\App\Services\Util::_number_format($file->total)." &euro; <br>
".self::getCellLabel($look, 'AZ').": ".\App\Services\Util::_number_format($file->deposit)." &euro; <br>
".self::getCellLabel($look, 'RZ').": ".\App\Services\Util::_number_format($file->final_payment)." &euro;";
$ret->date = \App\Services\Util::_format_date($file->updated_at, 'date');
break;
case 'storno':
$ret->name = "Stornobestätigung";
$ret->color = "danger";
$ret->cell = self::getCellLabel($look, 'SB').": ".\App\Services\Util::_number_format($file->total)." &euro;";
$ret->date = \App\Services\Util::_format_date($file->updated_at, 'date');
if($file->booking_document){
$ret->name .= " ".$file->booking_document->data->number;
$ret->cell = self::getCellLabel($look, 'SB').": ".\App\Services\Util::_number_format($file->storno)." &euro;";
$ret->route = $file->booking_document->getURL('file');
$ret->donwload = $file->booking_document->getURL('download');
$ret->opacity = false;
return $ret;
}
break;
case 'coupon':
$ret->name = "Gutschein ".$file->number;
$ret->color = "warning";
$ret->cell = self::getCellLabel($look, 'GW').": ".\App\Services\Util::_number_format($file->value)." &euro; |
bis: ".\App\Services\Util::_format_date($file->valid_date, 'date')." |
".(($file->is_redeemed) ? '<i class="fa fa-check-circle text-success"></i> '.\App\Services\Util::_format_date($file->redeem_date, 'date') : '<i class="fa fa-times-circle text-danger"></i>')."";
$ret->date = \App\Services\Util::_format_date($file->issue_date, 'date');
if($file->booking_document){
$ret->route = $file->booking_document->getURL('file');
$ret->donwload = $file->booking_document->getURL('download');
$ret->opacity = false;
return $ret;
}
break;
case 'voucher':
$ret->name = " Voucher ID ".$file->id;
$ret->color = "dark";
$ret->cell = self::getCellLabel($look, 'VC');
$ret->date = \App\Services\Util::_format_date($file->updated_at, 'date');
break;
case 'voucher_agency':
$ret->name = "Voucher-Agentur ID ".$file->id;
$ret->color = "dark";
$ret->cell = self::getCellLabel($look, 'VA');
$ret->date = \App\Services\Util::_format_date($file->updated_at, 'date');
break;
default:
$ret->name = "";
$ret->color = "";
$ret->cell = "";
$ret->date = "";
}
$ret->route = route('customer_file_show', [$identifier, $file->id]);
$ret->donwload = route('customer_file_show', [$identifier, $file->id, true]);
$ret->opacity = true;
return $ret;
}
public static function getCellLabel($look, $identifier){
switch($identifier){
case 'GP':
return $look === 'mail' ? 'GP' : 'Reise | Gesamtpreis';
break;
case 'AZ':
return $look === 'mail' ? 'AZ' : 'Anzahlung';
break;
case 'RZ':
return $look === 'mail' ? 'RZ' : 'Restzahlung';
break;
case 'SB':
return $look === 'mail' ? 'GB' : 'Storno | Betrag';
break;
case 'GW':
return $look === 'mail' ? 'W' : 'Gutschein | Wert';
break;
case 'VC':
return $look === 'mail' ? 'für Kunden' : 'Voucher für den Kunden';
break;
case 'VA':
return $look === 'mail' ? 'für Agentur' : 'Voucher für die Agentur';
break;
}
}
}

View file

@ -1,10 +1,8 @@
<?php
namespace App\Services;
namespace App\Helper;
use Form;
use App\Models\Draft;
use App\Models\Airline;
use App\Models\Country;
use App\Models\CMSAuthor;
use App\Models\DraftType;
use App\Models\Insurance;
@ -19,7 +17,6 @@ use App\Models\IQTravelGroup;
use App\Models\TravelCompany;
use App\Models\TravelCountry;
use App\Models\TravelProgram;
use App\Models\IQTravelGroupItem;
use App\Models\TravelNationality;
use App\Models\TravelBookingFewoChannel;
@ -52,6 +49,16 @@ class HTMLHelper
6 => 'Samstag',
];
private static $shortDays = [
0 => 'So',
1 => 'Mo',
2 => 'Di',
3 => 'Mi',
4 => 'Do',
5 => 'Fr',
6 => 'Sa',
];
public static $de_days = [
1 => 'Montag',
2 => 'Dienstag',
@ -75,7 +82,9 @@ class HTMLHelper
private static $salutation = [
1 => 'Herr',
2 => 'Frau',
3 => 'Firma',
3 => 'Divers/keine Anrede',
4 => 'Firma',
];
@ -87,6 +96,10 @@ class HTMLHelper
return self::$days[$i];
}
public static function getShortDay($i){
return self::$shortDays[$i];
}
public static function getDeDays(){
return self::$de_days;
}
@ -118,6 +131,18 @@ class HTMLHelper
}
public static function getSelectedOptions($values, $id = null, $empty = false){
$ret = "";
if($empty){
$ret .= '<option value="">'.$empty.'</option>\n';
}
foreach ($values as $key => $value){
$attr = ($key == $id) ? 'selected="selected"' : '';
$ret .= '<option value="'.$key.'" '.$attr.'>'.$value.'</option>\n';
}
return $ret;
}
public static function getActiveIcon($active){
return $active ? '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>' : '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>';
}
@ -141,7 +166,6 @@ class HTMLHelper
return $ret;
}
public static function getYearSelectOptions(){
$start = date("Y", strtotime("-5 years", time()));
$end = date("Y", strtotime("+1 years", time()));
@ -407,13 +431,18 @@ class HTMLHelper
return $ret;
}
public static function getWeekdaysString($weekdays = []){
public static function getWeekdaysString($weekdays = [], $short = false){
$ret = "";
if(count($weekdays)){
if(is_array($weekdays) && count($weekdays)){
foreach($weekdays as $weekday){
if($weekday !== NULL){
$ret .= self::getDay($weekday).', ';
if($short){
$ret .= self::getShortDay($weekday).', ';
}else{
$ret .= self::getDay($weekday).', ';
}
}
}
if($ret != ""){
@ -445,7 +474,10 @@ class HTMLHelper
$dom = new \DOMDocument('1.0', 'UTF-8');
//@$dom->loadHTML();
if($html == ""){
return "";
}
@$dom->loadHTML( mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
if(isset($arg['src'])){
@ -454,11 +486,13 @@ class HTMLHelper
foreach ($images as $image) {
$src = $image->getAttribute('src');
$host = config('app.url_stern');
if(strpos($src, $host) === false){
$src = $host."/".ltrim($src, '/');
if(strpos($src, 'http') === false){
$host = config('app.url_stern');
if(strpos($src, $host) === false){
$src = $host."/".ltrim($src, '/');
}
$image->setAttribute('src', $src);
}
$image->setAttribute('src', $src);
}
}

View file

@ -1,11 +1,11 @@
<?php
namespace App\Services;
namespace App\Helper;
use App\Models\IQContentSiteField;
use App\Models\IQContentTree;
use App\Models\IQContentTreeNode;
class HTMLTreeHelper
class TreeHTML
{
private static $uri = null;
@ -113,6 +113,7 @@ class HTMLTreeHelper
<h6 class="small font-weight-bold text-expanded text-uppercase">'.$categories.'</h6>';
} else {
/*
$site_field = IQContentSiteField::findSiteField($node, 'main-navi', 'menu-navi-image');
$img_src = (isset($site_field->content) ? $site_field->content : '/images/navi_images-assets/titelbild.jpg');
@ -120,6 +121,7 @@ class HTMLTreeHelper
<img src="'.$img_src.'" alt="" class="d-block ui-w-60">
<span class="media-body font-weight-semibold ml-2">' . ($link == $node->identifier ? '<strong>' : '') . $node->name . '</span>' . ($link == $node->identifier ? '</strong>' : '') . '
</a>';
*/
}
if ($children && $lvl == 0) {

View file

@ -3,199 +3,42 @@
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\Arrangement;
use App\Models\Booking;
use App\Models\BookingServiceItem;
use App\Models\Customer;
use App\Models\Lead;
use App\Models\Participant;
use App\Models\TravelBooking;
use App\Repositories\DraftRepository;
use App\Services\BookingImport;
class BookingController extends Controller
{
private $successStatus = 200;
private $successKey = 'f6077389c9ce710e554763a5de02c8ec';
private int $successStatus = 200;
private string $successKey;
protected $draftRepo;
public function __construct(DraftRepository $draftRepo)
public function __construct()
{
$this->draftRepo = $draftRepo;
$this->successKey = config('app.success_key');
}
public function import()
{
$request = \Request::all();
if($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']);
// $travel_booking = TravelBooking::find(2458);
if(!$travel_booking){
if (!$travel_booking) {
return response()->json(['error' => 'no-booking-found'], $this->successStatus);
}
// ---- createCustomer
$data = [
'salutation_id' => $travel_booking->salutation_id,
'name' => $travel_booking->last_name,
'firstname' => $travel_booking->first_name,
'street' => $travel_booking->street,
'zip' => $travel_booking->zipcode,
'city' => $travel_booking->city,
'country_id' => $travel_booking->country_id,
'phone' => $travel_booking->phone,
'phonemobile' => $travel_booking->mobile,
'email' => $travel_booking->email
];
$customer = Customer::create($data);
$booking = BookingImport::importFrom($travel_booking);
// ---- createLead
$data = [
'customer_id' => $customer->id,
'request_date' => $travel_booking->created,
'travelperiod_start' => $travel_booking->selected_start_date,
'travelperiod_end' => $travel_booking->selected_end_date,
'remarks' => $travel_booking->comments,
'sf_guard_user_id' => 15,
'is_closed' => true,
'initialcontacttype_id' => 14,
'status_id' => 7,
'website_id' => 1,
];
$lead = Lead::create($data);
$lead->updateNextDueDate();
$comfort = false;
if(isset($travel_booking->drafts['comfort']) && $travel_booking->drafts['comfort']){
$comfort = true;
}
$data = [
'booking_date' => $travel_booking->created->format('Y-m-d'),
'customer_id' => $customer->id,
'lead_id' => $lead->id,
'new_drafts' => $travel_booking->drafts === null ? 0 : 1,
'sf_guard_user_id' => 15,
'branch_id' => 4,
'travel_country_id' => isset($travel_booking->selected_travel['travel_country_id'][0]) ? $travel_booking->selected_travel['travel_country_id'][0] : null,
'travel_category_id' => isset($travel_booking->selected_travel['travel_category_id']) ? $travel_booking->selected_travel['travel_category_id'] : null,
'pax' => $travel_booking->selected_adults,
'title' => isset($travel_booking->selected_travel['travel_title']) ? $travel_booking->selected_travel['travel_title'] : "",
'comfort' => $comfort,
'start_date' => $travel_booking->selected_start_date->format('Y-m-d'),
'end_date' => $travel_booking->selected_end_date->format('Y-m-d'),
'website_id' => 1,
'travel_number' => isset($travel_booking->selected_travel['travel_number']) ? $travel_booking->selected_travel['travel_number'] : null,
/*'participant_name' => isset($travel_booking->participants[0]['last_name']) ? $travel_booking->participants[0]['last_name'] : null,
'participant_firstname' => isset($travel_booking->participants[0]['first_name']) ? $travel_booking->participants[0]['first_name'] : null,
'participant_birthdate' => isset($travel_booking->participants[0]['birthday']) ? date( "Y-m-d", strtotime($travel_booking->participants[0]['birthday'])) : null,
'participant_salutation_id' => isset($travel_booking->participants[0]['gender']) ? $travel_booking->participants[0]['gender'] : null,
'nationality_id' => isset($travel_booking->participants[0]['nationality']) ? $travel_booking->participants[0]['nationality'] : null,*/
'participant_name' => null,
'participant_firstname' => null,
'participant_birthdate' => null,
'participant_salutation_id' => null,
'nationality_id' => null,
'travel_company_id' => null,
'price' => $travel_booking->price,
'price_total' => $travel_booking->price_total,
'deposit_total' => $travel_booking->deposit_total,
'final_payment' => $travel_booking->final_payment,
'final_payment_date' => $travel_booking->final_payment_date->format('Y-m-d'),
'travelagenda_id' => isset($travel_booking->selected_travel['travelagenda_id']) ? $travel_booking->selected_travel['travelagenda_id'] : null,
];
//createBooking
$booking = Booking::create($data);
//createTraveler
if($travel_booking->participants){
foreach ($travel_booking->participants as $key => $participant){
Participant::create([
'booking_id' => $booking->id,
'participant_name' => $participant['last_name'],
'participant_firstname' => $participant['first_name'],
'participant_birthdate' => date( "Y-m-d", strtotime($participant['birthday'])),
'participant_salutation_id' => $participant['gender'],
'participant_child' => $participant['child'],
'nationality_id' =>$participant['nationality'],
]);
}
}
//createServiceItem //service_items
if($travel_booking->service_items){
foreach ($travel_booking->service_items as $key => $service_item){
BookingServiceItem::create([
'booking_id' => $booking->id,
'travel_company_id' => $service_item['travel_company_id'],
'service_price' => $service_item['service_price'],
'service_price_refund' => 0,
'commission' => $service_item['commission'],
'travel_date' => $service_item['travel_date'],
'name' => $service_item['name'],
'is_commission_locked' => 0,
]);
}
}
//createServiceItem //insurances
if($travel_booking->insurances){
foreach ($travel_booking->insurances as $key => $service_item){
BookingServiceItem::create([
'booking_id' => $booking->id,
'travel_company_id' => $service_item['travel_company_id'],
'service_price' => $service_item['price'],
'service_price_refund' => 0,
'commission' => $service_item['commission'],
'travel_date' => $service_item['travel_date'],
'name' => $service_item['name'],
'is_commission_locked' => 0,
]);
}
}
//createArrangement
if($travel_booking->arrangements) {
foreach ($travel_booking->arrangements as $key => $arrangement){
Arrangement::create([
'state' => isset($arrangement['state']) ? $arrangement['state'] : null,
'begin' => isset($arrangement['end']) ? $arrangement['end'] : null,
'end' => isset($arrangement['end']) ? $arrangement['end'] : null,
'type_s' => isset($arrangement['type_s']) ? $arrangement['type_s'] : null,
'data_s' => isset($arrangement['data_s']) ? $arrangement['data_s'] : null,
'view_position' => isset($arrangement['view_position']) ? $arrangement['view_position'] : null,
'booking_id' => $booking->id,
'type_id' => $arrangement['type_id'],
'in_pdf' => isset($arrangement['in_pdf']) ? $arrangement['in_pdf'] : null,
]);
}
}
//createDrafts
if($travel_booking->drafts) {
$this->draftRepo->create_drafts_from_booking($booking->id, $travel_booking->drafts);
}
$travel_booking->crm_booking_id = $booking->id;
$travel_booking->save();
$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' => $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);
}
}

View file

@ -6,7 +6,7 @@ use App\Http\Controllers\Controller;
use App\Models\CMSInfo;
use App\Models\CMSInfoAvailable;
use App\Models\CMSInfoHoliday;
use App\Services\HTMLHelper;
use App\Helper\HTMLHelper;
class CMSContentInfoController extends Controller

View file

@ -0,0 +1,185 @@
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Services\NavigationTreeService;
use Illuminate\Http\JsonResponse;
class NavigationController extends Controller
{
protected $navigationService;
public function __construct(NavigationTreeService $navigationService)
{
$this->navigationService = $navigationService;
}
/**
* Gibt den kompletten Navigationsbaum zurück
*
* @return JsonResponse
*/
public function getNavigationTree(): JsonResponse
{
try {
$tree = $this->navigationService->getNavigationTree();
return response()->json([
'success' => true,
'data' => $tree,
'meta' => [
'total_nodes' => $this->navigationService->countNodes($tree),
'generated_at' => now()->toIso8601String()
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
/**
* Gibt einen spezifischen Teil des Navigationsbaums zurück
*
* @param int $rootId Die ID des Root-Knotens
* @return JsonResponse
*/
public function getNavigationSubTree(int $rootId): JsonResponse
{
try {
$tree = $this->navigationService->getNavigationSubTree($rootId);
if (!$tree) {
return response()->json([
'success' => false,
'error' => 'Navigation node not found'
], 404);
}
return response()->json([
'success' => true,
'data' => $tree,
'meta' => [
'total_nodes' => $this->navigationService->countNodes([$tree]),
'generated_at' => now()->toIso8601String()
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
/**
* Gibt eine flache Liste aller Navigationspunkte zurück (ohne Hierarchie)
*
* @return JsonResponse
*/
public function getFlatNavigationList(): JsonResponse
{
try {
$list = $this->navigationService->getFlatNavigationList();
return response()->json([
'success' => true,
'data' => $list,
'meta' => [
'total_nodes' => count($list),
'generated_at' => now()->toIso8601String()
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
/**
* Gibt nur die aktiven Navigationspunkte zurück
*
* @return JsonResponse
*/
public function getActiveNavigationTree(): JsonResponse
{
try {
$tree = $this->navigationService->getNavigationTree(true);
return response()->json([
'success' => true,
'data' => $tree,
'meta' => [
'total_nodes' => $this->navigationService->countNodes($tree),
'generated_at' => now()->toIso8601String()
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
/**
* Gibt den Breadcrumb-Pfad für eine bestimmte Seite zurück
*
* @param int $pageId
* @return JsonResponse
*/
public function getBreadcrumb(int $pageId): JsonResponse
{
try {
$breadcrumb = $this->navigationService->getBreadcrumb($pageId);
if (empty($breadcrumb)) {
return response()->json([
'success' => false,
'error' => 'Page not found'
], 404);
}
return response()->json([
'success' => true,
'data' => $breadcrumb,
'meta' => [
'depth' => count($breadcrumb),
'generated_at' => now()->toIso8601String()
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
/**
* Löscht den Navigation-Cache
*
* @return JsonResponse
*/
public function clearCache(): JsonResponse
{
try {
$this->navigationService->clearCache();
return response()->json([
'success' => true,
'message' => 'Navigation cache cleared successfully'
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
}

View file

@ -30,8 +30,15 @@ class UserController extends Controller
return response()->json(['error' => 'Unauthorised'], 401);
}
return response()->json(['error' => 'Unauthorised', 'email' => request('email')], 401);
}
public function test()
{
return "testing";
}
/**
* Register api
* api.mein.sterntours.de

View file

@ -0,0 +1,396 @@
<?php
namespace App\Http\Controllers\Admin;
use Request;
use Carbon\Carbon;
use App\Models\Status;
use App\Services\Util;
use App\Models\Booking;
use App\Models\TravelCompany;
use App\Http\Controllers\Controller;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\ReportCollectionExport;
class ReportBookingController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function bookings()
{
$data = [
'filter_lead_status' => Status::get()->pluck('name', 'id')->toArray(),
'filter_travel_companies' => TravelCompany::where('active', true)->get()->pluck('name', 'id')->toArray(),
];
return view('admin.report.bookings', $data);
}
public function checkBookings()
{
$data = [
'filter_lead_status' => Status::get()->pluck('name', 'id')->toArray(),
'filter_travel_companies' => TravelCompany::where('active', true)->get()->pluck('name', 'id')->toArray(),
];
return view('admin.report.check_bookings', $data);
}
//checkBookingsDatatable
private function prozessBookingSearch()
{
$query = Booking::with( 'customer', 'lead', 'booking_strono','service_provider_entries', 'service_provider_entries.service_provider')->select('booking.*');
if(Request::get('filter_db_lead_status_id') != ""){
$query->whereHas('lead', function ($q) {
$q->whereIn('status_id', Request::get('filter_db_lead_status_id'));
});
}
if(Request::get('filter_lead_status_id') != ""){
$query->whereHas('lead', function ($q) {
$q->whereIn('status_id', Request::get('filter_lead_status_id'));
});
}
if(Request::get('filter_travel_company_id') != ""){
$query->whereIn("travel_company_id", Request::get('filter_travel_company_id'));
}
if(Request::get('filter_travel_date_from') != ""){
$travel_date_from = Carbon::parse(Request::get('filter_travel_date_from'))->format("Y-m-d");
$query->where("start_date", '>=', $travel_date_from);
}
if(Request::get('filter_travel_date_to') != ""){
$travel_date_to = Carbon::parse(Request::get('filter_travel_date_to'))->format("Y-m-d");
$query->where("start_date", '<=', $travel_date_to);
}
if(Request::get('filter_booking_date_from') != ""){
$filter_booking_date_from = Carbon::parse(Request::get('filter_booking_date_from'))->format("Y-m-d");
$query->where("booking_date", '>=', $filter_booking_date_from);
}
if(Request::get('filter_booking_date_to') != ""){
$filter_booking_date_from = Carbon::parse(Request::get('filter_booking_date_to'))->format("Y-m-d");
$query->where("booking_date", '<=', $filter_booking_date_from);
}
return $query;
}
public function bookingsDatatable()
{
$query = $this->prozessBookingSearch();
return \DataTables::eloquent($query)
->with('price_total_sum', function() use ($query) {
$price1 = with(clone $query)->whereNull('canceled')->sum('price');
$price2 = with(clone $query)->whereNotNull('canceled')->sum('price_canceled');
return Util::_number_format($price1+$price2);
})
->with('price_total_total_sum', function() use ($query) {
return Util::_number_format($query->sum('price_total'));
})
->with('proceed_total_sum', function() use ($query) {
if($query->count() > 200){
return 'max 200 ';
}
$all = $query->get();
$proceeds = 0;
foreach ($all as $v){
$proceeds += $v->proceeds(true);
}
return Util::_number_format($proceeds);
})
->addColumn('id', function (Booking $booking) {
return '<a data-order="' . $booking->id . '" href="' . route('booking_detail', [$booking->id]) . '" data-id="' . $booking->id . '">' . $booking->id . '</a>';
})
->addColumn('customer.fullName', function (Booking $booking) {
return $booking->customer->fullName();
})
->addColumn('price', function (Booking $booking) {
return $booking->booking_strono ? $booking->price_canceled : $booking->price;
})
->addColumn('proceeds', function (Booking $booking) {
return $booking->proceeds();
})
->addColumn('start_date', function (Booking $booking) {
return $booking->getStartDateFormat();
})
->addColumn('end_date', function (Booking $booking) {
return $booking->getEndDateFormat();
})
->addColumn('booking_date', function (Booking $booking) {
return $booking->getBookingDateFormat();
})
->addColumn('booking_strono_date', function (Booking $booking) {
return $booking->booking_strono ? $booking->booking_strono->storno_date->format("d.m.Y") : "";
})
->addColumn('service_provider.names', function (Booking $booking) {
$ret = "";
if($booking->service_provider_entries){
foreach ($booking->service_provider_entries as $service_provider_entry){
$ret .= '<span class="ui-stars">'.$service_provider_entry->service_provider->name." | ";
$ret .= $service_provider_entry->getAmountFinalEur()." | ";
$ret .= $service_provider_entry->is_cleared ? '<i class="fa fa-check text-success"></i>' : '<i class="fa fa-times text-danger"></i>';
$ret .= "</span><br>";
}
}
return $ret === "" ? "-" : $ret;
})
->addColumn('lead.status_id', function (Booking $booking) {
if($booking->lead && $booking->lead->status_id){
$color = $booking->lead->status->color;
$icon = "";
if($booking->lead->status_id == 14 && $booking->lead->is_rebook){
$color = '#94ae59';
$icon = '<i class="fa fa-check-circle"></i> ';
}
if($booking->lead->status_id == 14 && !$booking->lead->is_rebook){
$icon = '<i class="fa fa-times-circle"></i> ';
}
return '<span data-order="'.$booking->lead->status_id.'"><span class="badge badge-dark" style="background-color: '.$color.'">'.$icon.$booking->lead->status->name.'</span></span>';
}
return '<span data-order="0">-</span>';
})
->filterColumn('customer.fullName', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('customer', function ($q) use ($keyword) {
$q->where("name", 'LIKE', '%' . $keyword . '%')
->orWhere('firstname', 'LIKE', '%' . $keyword . '%');
});
}
})
->orderColumn('id', 'id $1')
->orderColumn('start_date', 'start_date $1')
->orderColumn('end_date', 'end_date $1')
->orderColumn('price', 'price $1')
->orderColumn('booking_date', 'booking_date $1')
->orderColumn('customer.fullName', 'customer.firstname $1')
->orderColumn('customer.firstname', 'customer.firstname $1')
->orderColumn('customer.name', 'customer.name $1')
//->orderColumn('lead.status_id', 'lead.status_id $1')
//->orderColumn('is_cleared', 'is_cleared $1')
->rawColumns(['id', 'lead.status_id', 'service_provider.names'])
->make(true);
}
public function bookingsExport(){
$query = $this->prozessBookingSearch();
$order = explode(",", Request::get('order'));
$orderByNum = [
0 => "id",
2 => "merlin_order_number",
3 => "price",
4 => "price_total",
7 => "start_date",
8 => "end_date",
9 => "booking_date",
];
if(isset($order[0])) {
$column = isset($orderByNum[$order[0]]) ? $orderByNum[$order[0]] : "start_date";
$direction = isset($order[1]) ? strtoupper($order[1]) : "ASC";
$query->orderBy($column, $direction);
}
$filename = "file-".date('Y-m-d-H-i-s');
$exports = $query->get();
$columns = [];
if(Request::get('export') === "export"){
$filename = "Buchungen_".date('Y-m-d-H-i-s');
$headers = array(
'BuchungsID',
'Status',
'Storno',
'MyJack Nr.',
'Organisation',
'Reisepreis',
'Erlös',
'Kunde',
'Reisedatum',
'bis',
'Buchungsdatum',
'Reiseland',
'Reiseprogramm',
'Reiseteilnehmer',
'Leistungsträger',
'Zahlung',
'Zahlungsdatum',
'Rechnungsnummer',
'abgeschlossen',
);
$total_price = 0;
$total_price_total = 0;
$total_proceeds = 0;
$total_amount_final = 0;
foreach($exports as $export) {
$storno_date = $export->booking_strono ? $export->booking_strono->storno_date->format("d.m.Y") : "";
if($export->service_provider_entries->count()){
$new = true;
foreach ($export->service_provider_entries as $service_provider_entry){
if($new){
$total_price += $export->isCanceled() ? $export->getPriceCanceledRaw() : $export->getPriceRaw();
$total_price_total += $export->getPriceTotalRaw();
$total_proceeds += $export->proceeds(true);
}
$total_amount_final += $service_provider_entry->getAmountFinalEurRaw();
$columns[] = array(
'BuchungsID' => $new ? $export->id : "",
'Status' => $new ? $export->lead->status->name : "",
'Storno' => $new ? $storno_date : "",
'MyJack Nr.' => $new ? $export->merlin_order_number : "",
'Organisation' => $new ? ($export->isCanceled() ? $export->price_canceled : $export->price) : "",
'Reisepreis' => $new ? $export->price_total : "",
'Erlös' => $new ? $export->proceeds() : "",
'Kunde' => $new ? $export->customer->fullName() : "",
'Reisedatum' => $new ? $export->getStartDateFormat() : "",
'bis' => $new ? $export->getEndDateFormat() : "",
'Buchungsdatum' => $new ? $export->getBookingDateFormat() : "",
'Reiseland' => $new && $export->travel_country ? $export->travel_country->name : "",
'Reiseprogramm' => $new && $export->travel_agenda ? $export->travel_agenda->name : "",
'Reiseteilnehmer' => $new ? $export->pax : "",
'Leistungsträger' => $service_provider_entry->service_provider->name,
'Zahlung' => $service_provider_entry->getAmountFinalEur(),
'Zahlungsdatum' => $service_provider_entry->getPaymentDateFormat(),
'Rechnungsnummer' => $service_provider_entry->invoice_number,
'abgeschlossen' => $service_provider_entry->is_cleared ? 'J' : 'N',
);
$new = false;
}
}else{
//$total_price += $export->booking->isCanceled() ? $export->booking->getPriceCanceledRaw() : $export->booking->getPriceRaw();
$total_price += $export->isCanceled() ? $export->getPriceCanceledRaw() : $export->getPriceRaw();
$columns[] = array(
'BuchungsID' => $export->id,
'Status' => $export->lead->status->name,
'Storno' => $storno_date,
'MyJack Nr.' => $export->merlin_order_number,
'Organisation' => $export->price,
'Organisation' => $export->isCanceled() ? $export->price_canceled : $export->price,
'Reisepreis' => $export->price_total,
'Erlös' => $export->proceeds(),
'Kunde' => $export->customer->fullName(),
'Reisedatum' => $export->getStartDateFormat(),
'bis' => $export->getEndDateFormat(),
'Buchungsdatum' => $export->getBookingDateFormat(),
'Reiseland' => $export->travel_country ? $export->travel_country->name : "",
'Reiseprogramm' => $export->travel_agenda ? $export->travel_agenda->name : "",
'Reiseteilnehmer' => $export->pax,
'Leistungsträger' => "",
'Zahlung' => "",
'Zahlungsdatum' => "",
'Rechnungsnummer' => "",
'abgeschlossen' => "",
);
}
}
$columns[] = array(
'BuchungsID' => "Total",
'Status' => "",
'Storno' => "",
'MyJack Nr.' => "",
'Organisation' => Util::_number_format($total_price),
'Reisepreis' => Util::_number_format($total_price_total),
'Erlös' => Util::_number_format($total_proceeds),
'Kunde' => "",
'Reisedatum' => "",
'bis' => "",
'Buchungsdatum' => "",
'Reiseland' => "",
'Reiseprogramm' => "",
'Reiseteilnehmer' => "",
'Leistungsträger' => "",
'Zahlung' => Util::_number_format($total_amount_final),
'Zahlungsdatum' => "",
'Rechnungsnummer' => "",
'abgeschlossen' => "",
);
}
return Excel::download(new ReportCollectionExport($columns, $headers), $filename.'.xls');
}
public function checkBookingsDatatable()
{
$query = $this->prozessBookingSearch();
return \DataTables::eloquent($query)
->addColumn('id', function (Booking $booking) {
return '<a data-order="' . $booking->id . '" href="' . route('booking_detail', [$booking->id]) . '" data-id="' . $booking->id . '">' . $booking->id . '</a>';
})
->addColumn('old_crm', function (Booking $booking) {
return '<a data-order="'.$booking->id.'" href="'.make_old_url('booking/'.$booking->id.'/edit').'" data-id="'.$booking->id.'">'.$booking->id.'</a>';
})
->addColumn('id', function (Booking $booking) {
return '<a data-order="' . $booking->id . '" href="' . route('booking_detail', [$booking->id]) . '" data-id="' . $booking->id . '">' . $booking->id . '</a>';
})
->addColumn('price', function (Booking $booking) {
return $booking->price;
})
->addColumn('service_total', function (Booking $booking) {
return $booking->getServiceTotal();
})
->addColumn('price_total', function (Booking $booking) {
return $booking->price_total;
})
->addColumn('check_total', function (Booking $booking) {
$check = $booking->getPriceRaw() + $booking->getServiceTotal(true);
return ($booking->getPriceTotalRaw() != $check) ? '<span class="badge badge-danger">'.Util::_number_format($check).'</span>' : "";
})
->addColumn('price_canceled', function (Booking $booking) {
return $booking->isCanceled() ? $booking->price_canceled : "";
})
->addColumn('start_date', function (Booking $booking) {
return $booking->getStartDateFormat();
})
->addColumn('end_date', function (Booking $booking) {
return $booking->getEndDateFormat();
})
->addColumn('booking_date', function (Booking $booking) {
return $booking->getBookingDateFormat();
})
->addColumn('lead.status_id', function (Booking $booking) {
if($booking->lead && $booking->lead->status_id){
$color = $booking->lead->status->color;
$icon = "";
if($booking->lead->status_id == 14 && $booking->lead->is_rebook){
$color = '#94ae59';
$icon = '<i class="fa fa-check-circle"></i> ';
}
if($booking->lead->status_id == 14 && !$booking->lead->is_rebook){
$icon = '<i class="fa fa-times-circle"></i> ';
}
return '<span data-order="'.$booking->lead->status_id.'"><span class="badge badge-dark" style="background-color: '.$color.'">'.$icon.$booking->lead->status->name.'</span></span>';
}
return '<span data-order="0">-</span>';
})
->orderColumn('id', 'id $1')
->orderColumn('old_crm', 'old_crm $1')
->orderColumn('start_date', 'start_date $1')
->orderColumn('end_date', 'end_date $1')
->orderColumn('price', 'price $1')
->orderColumn('booking_date', 'booking_date $1')
->orderColumn('customer.firstname', 'customer.firstname $1')
->orderColumn('customer.name', 'customer.name $1')
//->orderColumn('lead.status_id', 'lead.status_id $1')
//->orderColumn('is_cleared', 'is_cleared $1')
->rawColumns(['id', 'old_crm', 'check_total', 'lead.status_id'])
->make(true);
}
}

View file

@ -11,6 +11,7 @@ use App\Models\Status;
use App\Services\Util;
use App\Models\Booking;
use App\Models\TravelAgenda;
use App\Models\TravelCompany;
use App\Models\ServiceProvider;
use Illuminate\Validation\Rules\In;
use App\Http\Controllers\Controller;
@ -29,6 +30,8 @@ class ReportController extends Controller
{
$data = [
'filter_lead_status' => Status::get()->pluck('name', 'id')->toArray(),
'filter_travel_companies' => TravelCompany::where('active', true)->get()->pluck('name', 'id')->toArray(),
];
return view('admin.report.bookings', $data);
}
@ -38,10 +41,24 @@ class ReportController extends Controller
$data = [
'serviceProviders' => ServiceProvider::all(),
'filter_lead_status' => Status::get()->pluck('name', 'id')->toArray(),
'filter_travel_companies' => TravelCompany::where('active', true)->get()->pluck('name', 'id')->toArray(),
];
return view('admin.report.service_providers', $data);
}
public function checkBookings()
{
$data = [
'filter_lead_status' => Status::get()->pluck('name', 'id')->toArray(),
'filter_travel_companies' => TravelCompany::where('active', true)->get()->pluck('name', 'id')->toArray(),
];
return view('admin.report.check_bookings', $data);
}
//checkBookingsDatatable
private function prozessBookingSearch()
{
@ -59,6 +76,10 @@ class ReportController extends Controller
$q->whereIn('status_id', Request::get('filter_lead_status_id'));
});
}
if(Request::get('filter_travel_company_id') != ""){
$query->whereIn("travel_company_id", Request::get('filter_travel_company_id'));
}
if(Request::get('filter_travel_date_from') != ""){
$travel_date_from = Carbon::parse(Request::get('filter_travel_date_from'))->format("Y-m-d");
$query->where("start_date", '>=', $travel_date_from);
@ -86,7 +107,9 @@ class ReportController extends Controller
return \DataTables::eloquent($query)
->with('price_total_sum', function() use ($query) {
return Util::_number_format($query->sum('price'));
$price1 = with(clone $query)->whereNull('canceled')->sum('price');
$price2 = with(clone $query)->whereNotNull('canceled')->sum('price_canceled');
return Util::_number_format($price1+$price2);
})
->with('price_total_total_sum', function() use ($query) {
return Util::_number_format($query->sum('price_total'));
@ -108,6 +131,9 @@ class ReportController extends Controller
->addColumn('customer.fullName', function (Booking $booking) {
return $booking->customer->fullName();
})
->addColumn('price', function (Booking $booking) {
return $booking->booking_strono ? $booking->price_canceled : $booking->price;
})
->addColumn('proceeds', function (Booking $booking) {
return $booking->proceeds();
})
@ -161,6 +187,7 @@ class ReportController extends Controller
->orderColumn('id', 'id $1')
->orderColumn('start_date', 'start_date $1')
->orderColumn('end_date', 'end_date $1')
->orderColumn('price', 'price $1')
->orderColumn('booking_date', 'booking_date $1')
->orderColumn('customer.fullName', 'customer.firstname $1')
->orderColumn('customer.firstname', 'customer.firstname $1')
@ -230,7 +257,7 @@ class ReportController extends Controller
$new = true;
foreach ($export->service_provider_entries as $service_provider_entry){
if($new){
$total_price += $export->getPriceRaw();
$total_price += $export->isCanceled() ? $export->getPriceCanceledRaw() : $export->getPriceRaw();
$total_price_total += $export->getPriceTotalRaw();
$total_proceeds += $export->proceeds(true);
}
@ -240,7 +267,7 @@ class ReportController extends Controller
'Status' => $new ? $export->lead->status->name : "",
'Storno' => $new ? $storno_date : "",
'MyJack Nr.' => $new ? $export->merlin_order_number : "",
'Organisation' => $new ? $export->price : "",
'Organisation' => $new ? ($export->isCanceled() ? $export->price_canceled : $export->price) : "",
'Reisepreis' => $new ? $export->price_total : "",
'Erlös' => $new ? $export->proceeds() : "",
'Kunde' => $new ? $export->customer->fullName() : "",
@ -260,13 +287,16 @@ class ReportController extends Controller
$new = false;
}
}else{
$total_price += $export->getPriceRaw();
//$total_price += $export->booking->isCanceled() ? $export->booking->getPriceCanceledRaw() : $export->booking->getPriceRaw();
$total_price += $export->isCanceled() ? $export->getPriceCanceledRaw() : $export->getPriceRaw();
$columns[] = array(
'BuchungsID' => $export->id,
'Status' => $export->lead->status->name,
'Storno' => $storno_date,
'MyJack Nr.' => $export->merlin_order_number,
'Organisation' => $export->price,
'Organisation' => $export->isCanceled() ? $export->price_canceled : $export->price,
'Reisepreis' => $export->price_total,
'Erlös' => $export->proceeds(),
'Kunde' => $export->customer->fullName(),
@ -330,7 +360,11 @@ class ReportController extends Controller
});
});
}
if(Request::get('filter_travel_company_id') != ""){
$query->whereHas('booking', function ($q) {
$q->whereIn("travel_company_id", Request::get('filter_travel_company_id'));
});
}
if(Request::get('filter_is_cleared') != ""){
$query->where('is_cleared', '=', Request::get('filter_is_cleared'));
}
@ -366,8 +400,11 @@ class ReportController extends Controller
$price = 0;
$isset = [];
foreach ($all as $v){
$price += in_array($v->booking->lead_id, $isset) ? 0 : $v->booking->getPriceRaw();
$isset[] = $v->booking->lead_id;
if(!in_array($v->booking->inquiry_id, $isset)){
$price += $v->booking->isCanceled() ? $v->booking->getPriceCanceledRaw() : $v->booking->getPriceRaw();
}
$isset[] = $v->booking->inquiry_id;
}
return Util::_number_format($price);
})
@ -379,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);
})
@ -392,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();
@ -409,6 +446,9 @@ class ReportController extends Controller
->addColumn('booking.customer.fullName', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->customer->fullName();
})
->addColumn('booking.price', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->booking_strono ? $serviceProviderEntry->booking->price_canceled : $serviceProviderEntry->booking->price;
})
->addColumn('booking.proceeds', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->proceeds();
})
@ -447,6 +487,7 @@ class ReportController extends Controller
}
})
->orderColumn('booking.id', 'booking.id $1')
->orderColumn('booking.price', 'booking.price $1')
->orderColumn('booking.start_date', 'booking.start_date $1')
->orderColumn('booking.end_date', 'booking.end_date $1')
->orderColumn('booking.customer.firstname', 'booking.customer.firstname $1')
@ -513,7 +554,7 @@ class ReportController extends Controller
foreach($exports as $export) {
$new = in_array($export->booking->id, $isset) ? false : true;
if($new){
$total_price += $export->booking->getPriceRaw();
$total_price += $export->booking->isCanceled() ? $export->booking->getPriceCanceledRaw() : $export->booking->getPriceRaw();
$total_price_total += $export->booking->getPriceTotalRaw();
$total_proceeds += $export->booking->proceeds(true);
}
@ -521,10 +562,10 @@ 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->price : "",
'Organisation' => $new ? ($export->booking->isCanceled() ? $export->booking->price_canceled : $export->booking->price) : "",
'Gesamtreisepreis' => $new ? $export->booking->price_total : "",
'Reiseland' => $new && $export->booking->travel_country ? $export->booking->travel_country->name : "",
'Reiseprogramm' => $new && $export->booking->travel_agenda ? $export->booking->travel_agenda->name : "",
@ -589,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 : "",
@ -622,6 +663,75 @@ class ReportController extends Controller
}
return Excel::download(new ReportCollectionExport($columns, $headers), $filename.'.xls');
}
public function checkBookingsDatatable()
{
$query = $this->prozessBookingSearch();
return \DataTables::eloquent($query)
->addColumn('id', function (Booking $booking) {
return '<a data-order="' . $booking->id . '" href="' . route('booking_detail', [$booking->id]) . '" data-id="' . $booking->id . '">' . $booking->id . '</a>';
})
->addColumn('old_crm', function (Booking $booking) {
return '<a data-order="'.$booking->id.'" href="'.make_old_url('booking/'.$booking->id.'/edit').'" data-id="'.$booking->id.'">'.$booking->id.'</a>';
})
->addColumn('id', function (Booking $booking) {
return '<a data-order="' . $booking->id . '" href="' . route('booking_detail', [$booking->id]) . '" data-id="' . $booking->id . '">' . $booking->id . '</a>';
})
->addColumn('price', function (Booking $booking) {
return $booking->price;
})
->addColumn('service_total', function (Booking $booking) {
return $booking->getServiceTotal();
})
->addColumn('price_total', function (Booking $booking) {
return $booking->price_total;
})
->addColumn('check_total', function (Booking $booking) {
$check = $booking->getPriceRaw() + $booking->getServiceTotal(true);
return ($booking->getPriceTotalRaw() != $check) ? '<span class="badge badge-danger">'.Util::_number_format($check).'</span>' : "";
})
->addColumn('price_canceled', function (Booking $booking) {
return $booking->isCanceled() ? $booking->price_canceled : "";
})
->addColumn('start_date', function (Booking $booking) {
return $booking->getStartDateFormat();
})
->addColumn('end_date', function (Booking $booking) {
return $booking->getEndDateFormat();
})
->addColumn('booking_date', function (Booking $booking) {
return $booking->getBookingDateFormat();
})
->addColumn('lead.status_id', function (Booking $booking) {
if($booking->lead && $booking->lead->status_id){
$color = $booking->lead->status->color;
$icon = "";
if($booking->lead->status_id == 14 && $booking->lead->is_rebook){
$color = '#94ae59';
$icon = '<i class="fa fa-check-circle"></i> ';
}
if($booking->lead->status_id == 14 && !$booking->lead->is_rebook){
$icon = '<i class="fa fa-times-circle"></i> ';
}
return '<span data-order="'.$booking->lead->status_id.'"><span class="badge badge-dark" style="background-color: '.$color.'">'.$icon.$booking->lead->status->name.'</span></span>';
}
return '<span data-order="0">-</span>';
})
->orderColumn('id', 'id $1')
->orderColumn('old_crm', 'old_crm $1')
->orderColumn('start_date', 'start_date $1')
->orderColumn('end_date', 'end_date $1')
->orderColumn('price', 'price $1')
->orderColumn('booking_date', 'booking_date $1')
->orderColumn('customer.firstname', 'customer.firstname $1')
->orderColumn('customer.name', 'customer.name $1')
//->orderColumn('lead.status_id', 'lead.status_id $1')
//->orderColumn('is_cleared', 'is_cleared $1')
->rawColumns(['id', 'old_crm', 'check_total', 'lead.status_id'])
->make(true);
}
}

View file

@ -0,0 +1,205 @@
<?php
namespace App\Http\Controllers\Admin;
use Request;
use Response;
use HTMLHelper;
use Carbon\Carbon;
use App\Models\Status;
use App\Services\Util;
use App\Models\Booking;
use App\Models\FewoLodging;
use App\Models\TravelAgenda;
use App\Models\TravelCompany;
use App\Models\ServiceProvider;
use Illuminate\Validation\Rules\In;
use App\Http\Controllers\Controller;
use App\Models\ServiceProviderEntry;
use Maatwebsite\Excel\Facades\Excel;
use App\Models\TravelUserBookingFewo;
use App\Exports\ReportCollectionExport;
class ReportFewoController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function fewo()
{
$data = [
'filter_fewo_options' => FewoLodging::get()->pluck('name', 'id'),
];
return view('admin.report.fewo', $data);
}
//checkFewoDatatable
private function prozessFewoSearch()
{
$query = TravelUserBookingFewo::with('travel_booking_fewo_channel')->with('fewo_lodging')
->select('travel_user_booking_fewos.*')
->where('deleted_at', '=', null);
if(Request::get('filter_option_fewo_id') != ""){
$query->where('fewo_lodging_id', '=', Request::get('filter_option_fewo_id'));
}
if(Request::get('filter_date_from') != ""){
$date_from = Carbon::parse(Request::get('filter_date_from'))->format("Y-m-d");
$query->where("from_date", '>=', $date_from);
}
if(Request::get('filter_date_to') != ""){
$date_to = Carbon::parse(Request::get('filter_date_to'))->format("Y-m-d");
$query->where("from_date", '<=', $date_to);
}
if(Request::get('filter_booking_date_from') != ""){
$booking_date_from = Carbon::parse(Request::get('filter_booking_date_from'))->format("Y-m-d");
$query->where("booking_date", '>=', $booking_date_from);
}
if(Request::get('filter_booking_date_to') != ""){
$booking_date_to = Carbon::parse(Request::get('filter_booking_date_to'))->format("Y-m-d");
$query->where("booking_date", '<=', $booking_date_to);
}
return $query;
}
public function fewoDatatable()
{
$query = $this->prozessFewoSearch();
return \DataTables::eloquent($query)
->with('price_total_sum', function() use ($query) {
if($query->count() > 2000){
return 'max 2000 ';
}
$all = $query->get();
$proceeds = 0;
foreach ($all as $v){
$proceeds += $v->getPriceTotalRaw();
}
return Util::_number_format($proceeds);
})
->addColumn('id', function (TravelUserBookingFewo $travel_user_booking_fewo) {
return '<a data-order="'.$travel_user_booking_fewo->id.'" href="' . route('travel_user_booking_fewo_detail', [$travel_user_booking_fewo->id]) . '" class="">' . $travel_user_booking_fewo->id . '</a>';
// return '<a data-order="' . $travel_user_booking_fewo->id . '" href="' . route('booking_detail', [$travel_user_booking_fewo->id]) . '" data-id="' . $travel_user_booking_fewo->id . '">' . $travel_user_booking_fewo->id . '</a>';
})
->addColumn('travel_user', function (TravelUserBookingFewo $travel_user_booking_fewo) {
return '<a href="' . route('travel_user_detail', [$travel_user_booking_fewo->travel_user_id]) . '">'.$travel_user_booking_fewo->travel_user->first_name.' '.$travel_user_booking_fewo->travel_user->last_name.'</a>';
})
/* ->addColumn('start_date', function (TravelUserBookingFewo $travel_user_booking_fewo) {
return $travel_user_booking_fewo->getStartDateFormat();
})
->addColumn('end_date', function (TravelUserBookingFewo $travel_user_booking_fewo) {
return $travel_user_booking_fewo->getEndDateFormat();
})
/* ->addColumn('booking_date', function (TravelUserBookingFewo $travel_user_booking_fewo) {
return $travel_user_booking_fewo->getBookingDateFormat();
})
*/
->orderColumn('id', 'id $1')
->orderColumn('from_date', 'from_date $1')
->orderColumn('to_date', 'to_date $1')
->orderColumn('price_total', 'price_total $1')
->orderColumn('booking_date', 'booking_date $1')
->orderColumn('invoice_number', 'invoice_number $1')
//->orderColumn('lead.status_id', 'lead.status_id $1')
//->orderColumn('is_cleared', 'is_cleared $1')
->rawColumns(['id', 'travel_user'])
->make(true);
}
public function fewoExport(){
$query = $this->prozessFewoSearch();
$order = explode(",", Request::get('order'));
$orderByNum = [
0 => 'id',
1 => 'fewo_lodging.name',
2 => 'travel_user',
3 => 'from_date',
4 => 'to_date',
5 => 'adults',
6 => 'children',
7 => 'persons',
8 => 'price_total',
9 => 'booking_date',
10 => 'invoice_number',
];
if(isset($order[0])) {
$column = isset($orderByNum[$order[0]]) ? $orderByNum[$order[0]] : "id";
$direction = isset($order[1]) ? strtoupper($order[1]) : "ASC";
$query->orderBy($column, $direction);
}
$filename = "file-".date('Y-m-d-H-i-s');
$exports = $query->get();
$columns = [];
if(Request::get('export') === "export"){
$filename = "Buchungen_Fewo_".date('Y-m-d-H-i-s');
$headers = array(
'BuchungsID',
'FeWo',
'Kunde',
'vom',
'bis',
'Erwachsene',
'Kinder',
'Gesamt',
'Betrag',
'Buchungsdatum',
'R-Nr',
);
$total_price_total = 0;
foreach($exports as $export) {
$total_price_total += $export->getPriceTotalRaw();
$columns[] = array(
'BuchungsID' => $export->id,
'FeWo' => $export->fewo_lodging->name,
'Kunde' => $export->travel_user_id ? $export->travel_user->first_name.' '.$export->travel_user->last_name : " ",
'vom' => $export->from_date,
'bis' => $export->to_date,
'Erwachsene' => $export->adults,
'Kinder' => $export->children,
'Gesamt' => $export->persons,
'Betrag' => $export->price_total,
'Buchungsdatum' => $export->booking_date,
'R-Nr' => $export->invoice_number,
);
}
$columns[] = array(
'BuchungsID' => "Total",
'FeWo' => "",
'Kunde' => "",
'vom' => "",
'bis' => "",
'Erwachsene' => "",
'Kinder' => "",
'Gesamt' => Util::_number_format($total_price_total),
'Betrag' => "",
'Buchungsdatum' => "",
'R-Nr' => "",
);
}
return Excel::download(new ReportCollectionExport($columns, $headers), $filename.'.xls');
}
}

View file

@ -0,0 +1,217 @@
<?php
namespace App\Http\Controllers\Admin;
use Request;
use Response;
use HTMLHelper;
use Carbon\Carbon;
use App\Models\Lead;
use App\Models\Status;
use App\Services\Util;
use App\Models\Booking;
use App\Models\LeadMail;
use App\Models\FewoLodging;
use App\Models\TravelAgenda;
use App\Models\TravelCompany;
use App\Models\ServiceProvider;
use Illuminate\Validation\Rules\In;
use App\Http\Controllers\Controller;
use App\Models\ServiceProviderEntry;
use Maatwebsite\Excel\Facades\Excel;
use App\Models\TravelUserBookingFewo;
use App\Exports\ReportCollectionExport;
class ReportLeadsController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function leads()
{
// $query = Lead::with('customer')->with('sf_guard_user')->with('status')->select('lead.*');
$data = [
'filter_leads_options' => [], //need ??? FewoLodging::get()->pluck('name', 'id'),
];
return view('admin.report.leads', $data);
}
//checkLeadsDatatable
private function prozessLeadsSearch()
{
$query = Lead::with('customer')->with('sf_guard_user')->with('status')
->select('inquiries.*');
//->where('deleted_at', '=', null);
/*
if(Request::get('filter_option_leads_id') != ""){
$query->where('...?', '=', Request::get('filter_option_leads_id'));
}
*/
if(Request::get('filter_date_from') != ""){
$date_from = Carbon::parse(Request::get('filter_date_from'))->format("Y-m-d");
$query->where("request_date", '>=', $date_from);
}
if(Request::get('filter_date_to') != ""){
$date_to = Carbon::parse(Request::get('filter_date_to'))->format("Y-m-d");
$query->where("request_date", '<=', $date_to);
}
return $query;
}
public function leadsDatatable()
{
$query = $this->prozessLeadsSearch();
return \DataTables::eloquent($query)
->addColumn('id', function (Lead $lead) {
return '<a data-order="'.$lead->id.'" href="'.route('lead_detail', [$lead->id]).'" data-id="'.$lead->id.'">'.$lead->id.'</a>';
})
->addColumn('customer_id', function (Lead $lead) {
return '<a data-order="'.$lead->customer_id.'" href="'.route('customer_detail', [$lead->customer_id]).'" data-id="'.$lead->customer_id.'">'.$lead->customer_id.'</a>';
})
->addColumn('request_date', function (Lead $lead) {
return Carbon::parse($lead->request_date)->format(\Util::formatDateDB());
})
->addColumn('travel_country', function (Lead $lead) {
return $lead->getTravelCountryDestco();
})
->addColumn('status', function (Lead $lead) {
return $lead->getStatusBadge();
})
->addColumn('lead_notice', function (Lead $lead) {
return $lead->lead_notices->count() ? '<span data-order="1" class="badge badge-pill badge-success" data-lead_id="'.$lead->id.'" data-action="get_popover_lead_notice" data-placement="top" data-toggle="popover" title="letzte Notiz"><i class="fa fa-check"></i></span>' :
'<span data-order="0" class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>';
})
->addColumn('last_lead_email', function (Lead $lead) {
if($lead->lead_mails->count()){
$lead_mail = $lead->lead_mails_sent_at->last();
$badge = $lead_mail->is_answer ? 'badge-default' : 'badge-secondary';
$badge = !$lead_mail->send ? $badge : 'badge-success';
return '<a data-order="'.$lead_mail->getSentAtRaw().'" href="#" data-toggle="modal"
data-target="#modals-load-content"
data-id="show-mail"
data-url="mail"
data-preview="true"
data-lead_id="'.$lead->id.'"
data-lead_mail_id="'.$lead_mail->id.'"
data-action="show-lead-mail"
data-redirect="back"
data-route="'.route('lead_mail_modal_load').'">
<span class="badge '.$badge.'">'
.($lead_mail->send ? '<i class="fa fa-check-circle"></i>' : '<i class="fa fa-times-circle"></i>').' '
.$lead_mail->sent_at.'</span>
</a>';
}
return '<span data-order="">-</span>';
})
->addColumn('action_delete', function (Lead $lead) {
return '<a href="' . route('lead_delete', [$lead->id, 'lead']) . '" class="btn icon-btn btn-sm btn-danger" onclick="return confirm(\''.__('Really delete entry?').'\');"><span class="fa fa-trash"></span></a>';
})
->orderColumn('id', 'id $1')
->orderColumn('customer_id', 'customer_id $1')
->orderColumn('request_date', 'request_date $1')
->orderColumn('status', 'status_id $1')
->orderColumn('last_lead_email', function ($query, $order) {
$query->whereHas('lead_mails',
function ($q) use ($order) {
// $q->select('sent_at')->where('sent_at', DB::raw("(select max('sent_at') customer_mails)")); //)
})->orderBy(
LeadMail::select('sent_at')
->whereColumn('lead_id', 'inquiries.id')
->orderBy('sent_at', 'DESC')
->limit(1)
, $order);
})
->filterColumn('id', function($query, $keyword) {
if($keyword != ""){
$query->where('id', 'LIKE', '%'.$keyword.'%');
}
})
->filterColumn('customer_id', function($query, $keyword) {
if($keyword != ""){
$query->where('customer_id', 'LIKE', '%'.$keyword.'%');
}
})
->rawColumns(['action_edit', 'customer_id', 'sf_guard_user_id', 'id', 'status', 'last_lead_email', 'travel_country', 'lead_notice', 'action_delete'])
->make(true);
}
public function leadsExport(){
$query = $this->prozessLeadsSearch();
$order = explode(",", Request::get('order'));
$orderByNum = [
0 => 'id',
1 => 'customer_id',
2 => 'customer.firstname',
3 => 'customer.name',
4 => 'customer.email',
5 => 'request_date',
6 => 'travel_country',
7 => 'sf_guard_user.last_name',
8 => 'status',
9 => 'last_lead_email',
];
if(isset($order[0])) {
$column = isset($orderByNum[$order[0]]) ? $orderByNum[$order[0]] : "id";
$direction = isset($order[1]) ? strtoupper($order[1]) : "ASC";
$query->orderBy($column, $direction);
}
$filename = "file-".date('Y-m-d-H-i-s');
$exports = $query->get();
$columns = [];
if(Request::get('export') === "export"){
$filename = "Buchungen_Anfragen_".date('Y-m-d-H-i-s');
$headers = array(
'LeadID',
'KundenID',
'Vorname',
'Nachname',
'E-Mail',
'Anfrage-Datum',
'Reiseland',
'Sachbearbeiter',
'Status',
'Letzte E-Mail',
);
foreach($exports as $export) {
$columns[] = array(
'LeadID' => $export->id,
'KundenID' => $export->customer_id,
'Vorname' => $export->customer ? $export->customer->firstname : " ",
'Nachname' => $export->customer ? $export->customer->name : " ",
'E-Mail' => $export->customer ? $export->customer->email : " ",
'Anfrage-Datum' => _format_date($export->request_date),
'Reiseland' => $export->getTravelCountryDestco(false),
'Sachbearbeiter' => $export->sf_guard_user->last_name,
'Status' => $export->status->name,
'Letzte E-Mail' => $export->lead_mails->count() ? $export->lead_mails_sent_at->last()->sent_at : " ",
);
}
}
return Excel::download(new ReportCollectionExport($columns, $headers), $filename.'.xls');
}
}

View file

@ -0,0 +1,388 @@
<?php
namespace App\Http\Controllers\Admin;
use Request;
use Carbon\Carbon;
use App\Models\Status;
use App\Services\Util;
use App\Models\TravelCompany;
use App\Models\ServiceProvider;
use App\Http\Controllers\Controller;
use App\Models\ServiceProviderEntry;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\ReportCollectionExport;
class ReportProviderController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function providers()
{
$data = [
'serviceProviders' => ServiceProvider::all(),
'filter_lead_status' => Status::get()->pluck('name', 'id')->toArray(),
'filter_travel_companies' => TravelCompany::where('active', true)->get()->pluck('name', 'id')->toArray(),
];
return view('admin.report.service_providers', $data);
}
private function prozessProvidersSearch(){
$query = ServiceProviderEntry::with('booking', 'service_provider', 'booking.customer', 'booking.lead')->select('service_provider_entry.*')
->join('booking', 'service_provider_entry.booking_id', '=', 'booking.id' );
if(Request::get('filter_db_lead_status_id') != ""){
$query->whereHas('booking', function ($qe) {
$qe->whereHas('lead', function ($q) {
$q->whereIn('status_id', Request::get('filter_db_lead_status_id'));
});
});
}
if(Request::get('filter_lead_status_id') != ""){
$query->whereHas('booking', function ($qe) {
$qe->whereHas('lead', function ($q) {
$q->whereIn('status_id', Request::get('filter_lead_status_id'));
});
});
}
if(Request::get('filter_travel_company_id') != ""){
$query->whereHas('booking', function ($q) {
$q->whereIn("travel_company_id", Request::get('filter_travel_company_id'));
});
}
if(Request::get('filter_is_cleared') != ""){
$query->where('is_cleared', '=', Request::get('filter_is_cleared'));
}
if(Request::get('filter_service_provider_id') != ""){
$query->where('service_provider_id', '=', Request::get('filter_service_provider_id'));
}
if(Request::get('filter_travel_date_from') != ""){
$query->whereHas('booking', function ($q) {
$travel_date_from = Carbon::parse(Request::get('filter_travel_date_from'))->format("Y-m-d");
$q->where("start_date", '>=', $travel_date_from);
});
}
if(Request::get('filter_travel_date_to') != ""){
$query->whereHas('booking', function ($q) {
$travel_date_to = Carbon::parse(Request::get('filter_travel_date_to'))->format("Y-m-d");
$q->where("start_date", '<=', $travel_date_to);
});
}
if(Request::get('filter_booking_date_from') != ""){
$query->whereHas('booking', function ($q) {
$filter_booking_date_from = Carbon::parse(Request::get('filter_booking_date_from'))->format("Y-m-d");
$q->where("booking_date", '>=', $filter_booking_date_from);
});
}
if(Request::get('filter_booking_date_to') != ""){
$query->whereHas('booking', function ($q) {
$filter_booking_date_to = Carbon::parse(Request::get('filter_booking_date_to'))->format("Y-m-d");
$q->where("booking_date", '<=', $filter_booking_date_to);
});
}
return $query;
}
public function providersDatatable()
{
$query = $this->prozessProvidersSearch();
return \DataTables::eloquent($query)
->with('price_total_sum', function() use ($query) {
if($query->count() > 200){
return 'max 200 ';
}
$all = $query->get();
$price = 0;
$isset = [];
foreach ($all as $v){
if(!in_array($v->booking->inquiry_id, $isset)){
$price += $v->booking->isCanceled() ? $v->booking->getPriceCanceledRaw() : $v->booking->getPriceRaw();
}
$isset[] = $v->booking->inquiry_id;
}
return Util::_number_format($price);
})
->with('price_total_total_sum', function() use ($query) {
if($query->count() > 200){
return 'max 200 ';
}
$all = $query->get();
$price = 0;
$isset = [];
foreach ($all as $v){
$price += in_array($v->booking->inquiry_id, $isset) ? 0 : $v->booking->getPriceTotalRaw();
$isset[] = $v->booking->inquiry_id;
}
return Util::_number_format($price);
})
->with('proceed_total_sum', function() use ($query) {
if($query->count() > 200){
return 'max 200 ';
}
$all = $query->get();
$proceeds = 0;
$isset = [];
foreach ($all as $v){
$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();
$proceeds = 0;
foreach ($all as $v){
$proceeds += $v->proceeds(true);
}
return Util::_number_format($proceeds);*/
})
->addColumn('booking.id', function (ServiceProviderEntry $serviceProviderEntry) {
return '<a data-order="' . $serviceProviderEntry->booking->id . '" href="' . route('booking_detail', [$serviceProviderEntry->booking->id]) . '" data-id="' . $serviceProviderEntry->booking->id . '">' . $serviceProviderEntry->booking->id . '</a>';
})
->addColumn('booking.customer.fullName', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->customer->fullName();
})
->addColumn('booking.price', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->booking_strono ? $serviceProviderEntry->booking->price_canceled : $serviceProviderEntry->booking->price;
})
->addColumn('booking.proceeds', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->proceeds();
})
->addColumn('booking.start_date', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->getStartDateFormat();
})
->addColumn('booking.end_date', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->getEndDateFormat();
})
->addColumn('booking.booking_date', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->booking->getBookingDateFormat();
})
->addColumn('is_cleared', function (ServiceProviderEntry $serviceProviderEntry) {
return $serviceProviderEntry->is_cleared ? ' <span data-order="1" class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>' : '<span data-order="0" class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>';
})
->addColumn('booking.lead.status_id', function (ServiceProviderEntry $serviceProviderEntry) {
if($serviceProviderEntry->booking->lead && $serviceProviderEntry->booking->lead->status_id){
$color = $serviceProviderEntry->booking->lead->status->color;
$icon = "";
if($serviceProviderEntry->booking->lead->status_id == 14 && $serviceProviderEntry->booking->lead->is_rebook){
$color = '#94ae59';
$icon = '<i class="fa fa-check-circle"></i> ';
}
if($serviceProviderEntry->booking->lead->status_id == 14 && !$serviceProviderEntry->booking->lead->is_rebook){
$icon = '<i class="fa fa-times-circle"></i> ';
}
return '<span data-order="'.$serviceProviderEntry->booking->lead->status_id.'"><span class="badge badge-dark" style="background-color: '.$color.'">'.$icon.$serviceProviderEntry->booking->lead->status->name.'</span></span>';
}
return '<span data-order="0">-</span>';
})
->filterColumn('booking.customer.fullName', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('booking', function ($q) use ($keyword) {
$q->whereHas('customer', function ($q) use ($keyword) {
$q->where("name", 'LIKE', '%' . $keyword . '%')
->orWhere('firstname', 'LIKE', '%' . $keyword . '%');
});
});
}
})
->orderColumn('booking.id', 'booking.id $1')
->orderColumn('booking.price', 'booking.price $1')
->orderColumn('booking.start_date', 'booking.start_date $1')
->orderColumn('booking.end_date', 'booking.end_date $1')
->orderColumn('booking.booking_date', 'booking.booking_date $1')
->orderColumn('booking.customer.firstname', 'booking.customer.firstname $1')
->orderColumn('booking.customer.name', 'booking.customer.name $1')
->orderColumn('is_cleared', 'is_cleared $1')
->rawColumns(['is_cleared', 'booking.id', 'booking.lead.status_id'])
->make(true);
}
public function providersExport()
{
$query = $this->prozessProvidersSearch();
$order = explode(",", Request::get('order'));
$orderByNum = [
0 => "id",
1 => "booking.id", //booking
4 => "booking.merlin_order_number",//booking
5 => "booking.price",//booking
6 => "booking.price_total",//booking
9 => "booking.start_date",//booking
10 => "booking.end_date",//booking
11 => "booking.booking_date",//booking
12 => "is_cleared",
];
if(isset($order[0])){
$column = isset($orderByNum[$order[0]]) ? $orderByNum[$order[0]] : "start_date";
$direction = isset($order[1]) ? strtoupper($order[1]) : "ASC";
$query->orderBy($column, $direction);;
}
$filename = "file-".date('Y-m-d-H-i-s');
$exports = $query->get();
$ctemps = [];
$columns = [];
if(Request::get('export') === "export"){
$filename = "Export_".date('Y-m-d-H-i-s');
$headers = array(
'Zähler',
'MyJack Nr.',
'CRM Nr',
'Kunde',
'Reisedatum',
'bis',
'Buchungsdatum',
'Organisation',
'Gesamtreisepreis',
'Reiseland',
'Reiseprogramm',
'Reiseteilnehmer',
'Leistungsträger',
'Rechnungsnummer',
'Zahlung',
'Zahlungsdatum',
'Erlös',
'Konto',
);
$isset = [];
$total_price = 0;
$total_price_total = 0;
$total_amount_final = 0;
$total_proceeds = 0;
foreach($exports as $export) {
$new = in_array($export->booking->id, $isset) ? false : true;
if($new){
$total_price += $export->booking->isCanceled() ? $export->booking->getPriceCanceledRaw() : $export->booking->getPriceRaw();
$total_price_total += $export->booking->getPriceTotalRaw();
$total_proceeds += $export->booking->proceeds(true);
}
$total_amount_final += $export->getAmountFinalEurRaw();
$ctemps[$export->booking->id][] = array(
'Zähler' => $new ? $export->getCounter() : "",
'MyJack Nr.' => $new ? $export->booking->merlin_order_number : "",
'CRM Nr' => $new ? $export->booking->inquiry_id : "",
'Kunde' => $new ? $export->booking->customer->name : "",
'Reisedatum' => $new ? $export->booking->getStartDateFormat() : "",
'bis' => $new ? $export->booking->getEndDateFormat() : "",
'Buchungsdatum' => $export->booking->getBookingDateFormat(),
'Organisation' => $new ? ($export->booking->isCanceled() ? $export->booking->price_canceled : $export->booking->price) : "",
'Gesamtreisepreis' => $new ? $export->booking->price_total : "",
'Reiseland' => $new && $export->booking->travel_country ? $export->booking->travel_country->name : "",
'Reiseprogramm' => $new && $export->booking->travel_agenda ? $export->booking->travel_agenda->name : "",
'Reiseteilnehmer' => $new ? $export->booking->pax : "",
'Leistungsträger' => $export->service_provider->name,
'Rechnungsnummer' => $export->invoice_number,
'Zahlung' => $export->getAmountFinalEur(),
'Zahlungsdatum' => $export->getPaymentDateFormat(),
'Erlös' => $new ? $export->booking->proceeds() : "",
'Konto' => $export->booking->getKontoNumber()
);
$isset[] = $export->booking->id;
}
foreach($ctemps as $bid => $value){
$columns = array_merge($columns, $value);
}
$columns[] = array(
'Zähler' => "Total",
'MyJack Nr.' => "",
'CRM Nr' => "",
'Kunde' =>"",
'Reisedatum' => "",
'bis' => "",
'Buchungsdatum' => "",
'Organisation' => Util::_number_format($total_price),
'Gesamtreisepreis' => Util::_number_format($total_price_total),
'Reiseland' => "",
'Reiseprogramm' => "",
'Reiseteilnehmer' => "",
'Leistungsträger' => "",
'Rechnungsnummer' => "",
'Zahlung' => Util::_number_format($total_amount_final),
'Zahlungsdatum' => "",
'Erlös' => Util::_number_format($total_proceeds),
'Konto' => ""
);
}
if(Request::get('export') === "export_lt"){
$filename = "Export_LT_".date('Y-m-d-H-i-s');
$headers = array(
'Zähler',
'MyJack Nr.',
'CRM Nr',
'Kunde',
'Reisedatum',
'bis',
'Buchungsdatum',
'Reiseland',
'Reiseprogramm',
'Reiseteilnehmer',
'Leistungsträger',
'Rechnungsnummer',
'Zahlung',
'ZahlungVorgang',
);
$isset = [];
$total_amount_final = 0;
$payments_total = 0;
foreach($exports as $export) {
$new = in_array($export->booking->id, $isset) ? false : true;
if($new) {
$payments_total += $export->booking->getServiceProviderPaymentsTotal(true);
}
$total_amount_final += $export->getAmountFinalEurRaw();
$ctemps[$export->booking->id][] = array(
'Zähler' => $new ? $export->getCounter() : "",
'MyJack Nr.' => $new ? $export->booking->merlin_order_number : "",
'CRM Nr' => $new ? $export->booking->inquiry_id : "",
'Kunde' => $new ? $export->booking->customer->name : "",
'Reisedatum' => $new ? $export->booking->getStartDateFormat() : "",
'bis' => $new ? $export->booking->getEndDateFormat() : "",
'Buchungsdatum' => $new ? $export->booking->getBookingDateFormat() : "",
'Reiseland' => $new && $export->booking->travel_country ? $export->booking->travel_country->name : "",
'Reiseprogramm' => $new && $export->booking->travel_agenda ? $export->booking->travel_agenda->name : "",
'Reiseteilnehmer' => $new ? $export->booking->pax : "",
'Leistungsträger' => $export->service_provider->name,
'Rechnungsnummer' => $export->invoice_number,
'Zahlung' => $export->getAmountFinalEur(),
'ZahlungVorgang' => $export->booking->getServiceProviderPaymentsTotal(),
);
$isset[] = $export->booking->id;
}
foreach($ctemps as $bid => $value){
$columns = array_merge($columns, $value);
}
$columns[] = array(
'Zähler' => "Total",
'MyJack Nr.' => "",
'CRM Nr' => "",
'Kunde' => "",
'Reisedatum' => "",
'bis',
'Buchungsdatum' => "",
'Reiseland' => "",
'Reiseprogramm' => "",
'Reiseteilnehmer' => "",
'Leistungsträger' => "",
'Rechnungsnummer' => "",
'Zahlung' => Util::_number_format($total_amount_final),
'ZahlungVorgang' => Util::_number_format($payments_total),
);
}
return Excel::download(new ReportCollectionExport($columns, $headers), $filename.'.xls');
}
}

View file

@ -9,7 +9,7 @@ use App\User;
use Validator;
use DataTables;
use App\Models\Account;
use App\Services\HTMLHelper;
use App\Helper\HTMLHelper;
use App\Services\MyGoogle2FA;
use App\Mail\MailVerifyContact;
use App\Repositories\UserRepository;
@ -36,20 +36,19 @@ class AdminUserController extends Controller
];
$user = User::findOrFail(8);
/* $MyGoogle2FA = new MyGoogle2FA();
/* $MyGoogle2FA = new MyGoogle2FA();
$valid = $MyGoogle2FA->init($user)->check2Fa('676493');
dd($valid); */
dd($valid); */
return view('admin.users', $data);
}
public function edit($id)
{
if($id == "new"){
if ($id == "new") {
$user = new User();
}else{
} else {
$user = User::findOrFail($id);
}
/*if(!$user->account){
$user->account = new Account();
@ -60,21 +59,19 @@ class AdminUserController extends Controller
'isFromAdmin' => 'true',
];
return view('admin.user_edit', $data);
}
public function store()
{
$data = Request::all();
if($data['user_id'] === "new"){
if ($data['user_id'] === "new") {
$rules = array(
'name' => 'required',
'email' => 'required|string|email|max:255|unique:users',
'email-confirm' => 'required|same:email',
);
}else{
} else {
$rules = array(
'name' => 'required'
);
@ -84,27 +81,24 @@ class AdminUserController extends Controller
return back()->withRequest(Request::all())->withErrors($validator);
}
if($data['user_id'] === "new"){
if ($data['user_id'] === "new") {
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => env('APP_KEY'),
]);
$unique = false;
do{
do {
$confirmation_code = str_random(30);
if( User::where('confirmation_code', '=', $confirmation_code)->count() == 0){
if (User::where('confirmation_code', '=', $confirmation_code)->count() == 0) {
$unique = true;
}
}
while(!$unique);
} while (!$unique);
$user->confirmation_code = $confirmation_code;
$user->save();
Mail::to($user->email)->send(new MailVerifyContact($confirmation_code, $user));
}else{
} else {
$user = User::findOrFail($data['user_id']);
$user->name = $data['name'];
$user->save();
@ -121,54 +115,54 @@ class AdminUserController extends Controller
\Session()->flash('alert-success', "Kontakt gelöscht");
return redirect('/admin/users');
}
public function loadModal($id){
public function loadModal($id)
{
if(Request::ajax()) {
if (Request::ajax()) {
$data = Request::all();
$user = User::findOrFail($id);
if(isset($data['action'])){
if($data['action'] === 'show-user-roles'){
if (isset($data['action'])) {
if ($data['action'] === 'show-user-roles') {
$fill = [
'user' => $user,
'action' => $data['action'],
'groups' => config('permissions.groups'),
'roles' => config('permissions.roles')
];
return view("admin.user_modal", $fill )->render();
return view("admin.user_modal", $fill)->render();
}
if($data['action'] === 'show-user-active'){
if ($data['action'] === 'show-user-active') {
$fill = [
'user' => $user,
'action' => $data['action'],
];
return view("admin.active_modal", $fill )->render();
return view("admin.active_modal", $fill)->render();
}
if($data['action'] === 'show-user-google2fa'){
if($user->isGoogle2Fa()){
if ($data['action'] === 'show-user-google2fa') {
if ($user->isGoogle2Fa()) {
$MyGoogle2FA = new MyGoogle2FA();
$MyGoogle2FA->init($user);
$MyGoogle2FA->init($user);
$fill = [
'user' => $user,
'action' => 'delete-user-google2fa',
'MyGoogle2FA' => $MyGoogle2FA,
];
return view("admin.google2fa_delete_modal", $fill )->render();
}else{
return view("admin.google2fa_delete_modal", $fill)->render();
} else {
$MyGoogle2FA = new MyGoogle2FA();
$MyGoogle2FA->init($user)->generate();
$MyGoogle2FA->init($user)->generate();
$fill = [
'user' => $user,
'action' => 'activate-user-google2fa',
'MyGoogle2FA' => $MyGoogle2FA,
];
return view("admin.google2fa_modal", $fill )->render();
return view("admin.google2fa_modal", $fill)->render();
}
}
}
@ -176,9 +170,10 @@ class AdminUserController extends Controller
return false;
}
public function updateModal($action = false){
public function updateModal($action = false)
{
if($action=== 'show-user-roles'){
if ($action === 'show-user-roles') {
$data = Request::all();
$user = User::findOrFail($data['id']);
$user->permissions = isset($data['permissions']) ? $data['permissions'] : [];
@ -187,17 +182,15 @@ class AdminUserController extends Controller
$user->active = isset($data['active']) ? true : false;
$user->save();
\Session()->flash('alert-save', true);
}
if($action=== 'show-user-active'){
if ($action === 'show-user-active') {
$data = Request::all();
$user = User::findOrFail($data['id']);
$user->active = isset($data['active']) ? true : false;
$user->save();
\Session()->flash('alert-save', true);
}
if($action=== 'activate-user-google2fa'){
if ($action === 'activate-user-google2fa') {
$data = Request::all();
$user = User::findOrFail($data['id']);
$user->google2fa = true;
@ -205,7 +198,7 @@ class AdminUserController extends Controller
\Session()->flash('alert-save', true);
}
if($action=== 'delete-user-google2fa'){
if ($action === 'delete-user-google2fa') {
$data = Request::all();
$user = User::findOrFail($data['id']);
$user->google2fa = false;
@ -228,23 +221,22 @@ class AdminUserController extends Controller
return '<a href="' . route('admin_user_edit', [$user->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('admin', function (User $user) {
return '<a href="#" data-url="'.route('admin_user_load_modal', $user->id).'" data-data="'.$user->id.'" data-action="show-user-roles" class="update_modal_data_show">'.HTMLHelper::getRoleLabel($user->admin, '<i class="fa fa-edit"></i> Rechte + ','').'</a>';
return '<a href="#" data-url="' . route('admin_user_load_modal', $user->id) . '" data-data="' . $user->id . '" data-action="show-user-roles" class="update_modal_data_show">' . HTMLHelper::getRoleLabel($user->admin, '<i class="fa fa-edit"></i> Rechte + ', '') . '</a>';
})
->addColumn('google2fa', function (User $user) {
$icon = $user->google2fa ? '<i class="fa fa-check-circle"></i>' : '<i class="fa fa-times-circle"></i>';
$color = $user->google2fa ? 'primary' : 'danger';
return ' <a href="#" data-url="'.route('admin_user_load_modal', $user->id).'" data-data="'.$user->id.'" data-action="show-user-google2fa" class="update_modal_data_show btn btn-sm btn-'.$color.'">'.$icon.' google2fa</a>';
return ' <a href="#" data-url="' . route('admin_user_load_modal', $user->id) . '" data-data="' . $user->id . '" data-action="show-user-google2fa" class="update_modal_data_show btn btn-sm btn-' . $color . '">' . $icon . ' google2fa</a>';
})
->addColumn('confirmed', function (User $user) {
return $user->confirmed ? '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>' : '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>';
})
->addColumn('active', function (User $user) {
$active = $user->active ? '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>' : '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>';
return ' <a href="#" data-url="'.route('admin_user_load_modal', $user->id).'" data-data="'.$user->id.'" data-action="show-user-active" class="update_modal_data_show">'.$active.'</a>';
return ' <a href="#" data-url="' . route('admin_user_load_modal', $user->id) . '" data-data="' . $user->id . '" data-action="show-user-active" class="update_modal_data_show">' . $active . '</a>';
})
->addColumn('action_delete', function (User $user) {
return '<a href="' . route('admin_user_delete', [$user->id]) . '" class="btn icon-btn btn-sm btn-danger" onclick="return confirm(\''.__('Really delete entry?').'\');"><span class="fa fa-trash"></span></a>';
return '<a href="' . route('admin_user_delete', [$user->id]) . '" class="btn icon-btn btn-sm btn-danger" onclick="return confirm(\'' . __('Really delete entry?') . '\');"><span class="fa fa-trash"></span></a>';
})
->orderColumn('confirmed', 'confirmed $1')
->orderColumn('active', 'active $1')
@ -252,8 +244,4 @@ class AdminUserController extends Controller
->rawColumns(['action_edit', 'admin', 'confirmed', 'active', 'action_delete', 'google2fa'])
->make(true);
}
}
}

View file

@ -46,6 +46,7 @@ class LoginController extends Controller
$user->last_login = date('Y-m-d H:i:s');
$user->save();
}
protected function handleUserWasAuthenticated(Request $request, $throttles)
{

View file

@ -2,30 +2,35 @@
namespace App\Http\Controllers;
use Request;
use Illuminate\Http\Request;
use App\Models\Booking;
use App\Models\BookingFile;
use App\Models\Participant;
use App\Models\BookingNotice;
use App\Models\TravelCompany;
use App\Models\ServiceProvider;
use App\Models\BookingDraftItem;
use App\Models\BookingServiceItem;
use App\Models\Participant;
use App\Models\ServiceProviderEntry;
use App\Models\TravelCompany;
use App\Repositories\DraftRepository;
use App\Repositories\BookingRepository;
use App\Repositories\CustomerRepository;
use App\Repositories\BookingPDFRepository;
use App\Repositories\BookingFileRepository;
use App\Repositories\CustomerFileRepository;
use App\Repositories\CustomerMailRepository;
class BookingController extends Controller
{
protected $bookingRepo;
protected $custRepo;
public function __construct(BookingRepository $bookingRepo)
{
public function __construct(BookingRepository $bookingRepo, CustomerRepository $custRepo)
{
$this->middleware(['admin', '2fa']);
$this->bookingRepo = $bookingRepo;
$this->custRepo = $custRepo;
}
public function index($step = false)
@ -39,11 +44,10 @@ class BookingController extends Controller
public function detail($id)
{
if($id == "new") {
if ($id == "new") {
$booking = new Booking();
$id = 'new';
}else{
} else {
$booking = Booking::findOrFail($id);
$booking->getPassolutionPDF(true);
$id = $booking->id;
@ -54,178 +58,231 @@ class BookingController extends Controller
'show_modal_quill_preview' => true,
];
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($data['action'] === 'save_notice'){
$booking = $this->bookingRepo->updateNotice($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingNotice");
}
if($data['action'] === 'edit_notice'){
$booking = $this->bookingRepo->updateNotice($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingNotice");
}
if($data['action'] === 'save_lead_status'){
$booking = $this->bookingRepo->updateLeadStatus($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingLead");
}
if($data['action'] === 'update_booking'){
$booking = $this->bookingRepo->updateBooking($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingBooking");
}
if($data['action'] === 'update_booking_services'){
$booking = $this->bookingRepo->updateBookingServices($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingServices");
}
if($data['action'] === 'update_booking_number'){
$booking = $this->bookingRepo->updateBookingNumber($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingMyJack");
}
if($data['action'] === 'update_booking_price'){
$booking = $this->bookingRepo->updateBookingPrice($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingPrice");
}
if($data['action'] === 'update_service_provider_entry'){
$booking = $this->bookingRepo->updateServiceProviderEntry($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingProvider");
}
if($data['action'] === 'update_booking_service_item'){
$booking = $this->bookingRepo->updateBookingServiceItem($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingProvider");
}
if($data['action'] === 'update_booking_participant'){
$booking = $this->bookingRepo->updateBookingParticipant($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingParticipant");
}
if($id === "new") {
if ($id === "new") {
$booking = new Booking();
}else{
} else {
$booking = Booking::findOrFail($id);
}
// $booking->merlin_order_number = $data['merlin_order_number'];
// $booking->save();
$i = 1;
if($data['action'] === 'addItemUp'){
if ($data['action'] === 'convertArrangementsToDrafts') {
if (!$booking->arrangements || $booking->arrangements->count() == 0) {
\Session()->flash('alert-warning', __('Keine Arrangements zum Konvertieren vorhanden'));
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
$this->bookingRepo->convertArrangementsToDrafts($booking);
\Session()->flash('alert-success', __('Arrangements wurden erfolgreich in die neue Draft-Struktur übernommen'));
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
if ($data['action'] === 'loadDraftToBooking') {
if (!isset($data['draft_id']) || !$data['draft_id']) {
\Session()->flash('alert-error', __('Keine Vorlage ausgewählt'));
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
$this->bookingRepo->loadDraftToBooking($booking->id, $data['draft_id']);
\Session()->flash('alert-success', __('Vorlage wurde erfolgreich geladen'));
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
if ($data['action'] === 'deleteAllChecked') {
if (!isset($data['draft_item_delete']) || !$data['draft_item_delete']) {
\Session()->flash('alert-error', 'Es wurden keine Leistungen ausgewählt');
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
foreach ($data['draft_item_delete'] as $draft_item_delete_id => $v) {
$booking_draft_item = BookingDraftItem::findOrFail($draft_item_delete_id);
if ($booking_draft_item->booking_id === $booking->id) {
$booking_draft_item->delete();
}
}
$booking->calculate_price_total();
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
if ($data['action'] === 'save_notice') {
$booking = $this->bookingRepo->updateNotice($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingNotice");
}
if ($data['action'] === 'edit_notice') {
$booking = $this->bookingRepo->updateNotice($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingNotice");
}
if (strpos($data['action'], 'createPDF') !== false) {
$bookingPDF = new BookingPDFRepository($booking);
$bookingPDF->createPDF($id, $data);
\Session()->flash('alert-success', 'PDF Datei erstellt');
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingFiles");
}
$redirect = route('booking_detail', [$booking->id]);
$i = 1;
if ($data['action'] === 'addItemUp') {
$travel_program_id = null;
$request_date = null;
$comfort = 0;
if(count($booking->booking_draft_items)){
if (count($booking->booking_draft_items)) {
$first_booking_draft_item = $booking->booking_draft_items()->first();
$travel_program_id = $first_booking_draft_item->travel_program_id;
$request_date = $first_booking_draft_item->request_date;
$comfort = $first_booking_draft_item->comfort;
}
$booking->booking_draft_items()->create([
'booking_id' => $booking->id,
'travel_program_id' => $travel_program_id,
'fewo_lodging_id' => null,
'travel_class_id' => null,
'draft_item_id' => null,
'draft_type_id' => null,
'request_date' => $request_date,
'days_start' => null,
'days_duration' => null,
'start_date' => null,
'end_date' => null,
'service' => '',
'price_adult' => null,
'adult' => null ,
'price_children' => 0,
'children' => 0,
'price' => 0,
'pos' => $i,
'in_pdf' => true,
'comfort' => $comfort
]);
$i++;
$add_draft_items_up_number = isset($data['add_draft_items_up_number']) ? intval($data['add_draft_items_up_number']) : 1;
for ($j = 0; $j < $add_draft_items_up_number; $j++) {
$booking->booking_draft_items()->create([
'booking_id' => $booking->id,
'travel_program_id' => $travel_program_id,
'fewo_lodging_id' => null,
'travel_class_id' => null,
'draft_item_id' => null,
'draft_type_id' => null,
'request_date' => $request_date,
'days_start' => null,
'days_duration' => null,
'start_date' => null,
'end_date' => null,
'service' => '',
'price_adult' => null,
'adult' => null,
'price_children' => 0,
'children' => 0,
'price' => 0,
'pos' => $i,
'in_pdf' => true,
'comfort' => $comfort
]);
$i++;
}
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
if(isset($data['draft_item'])){
foreach ($data['draft_item'] as $booking_draft_item_id => $draft_item){
if (isset($data['draft_item'])) {
foreach ($data['draft_item'] as $booking_draft_item_id => $draft_item) {
$BookingDraftItem = BookingDraftItem::findOrFail($booking_draft_item_id);
$draft_item['price_adult'] = isset($draft_item['price_adult']) ? $draft_item['price_adult'] : null;
$draft_item['adult'] = isset($draft_item['adult']) ? $draft_item['adult'] : null;
$draft_item['price_children'] = isset($draft_item['price_children']) ? $draft_item['price_children'] : null;
$draft_item['children'] = isset($draft_item['children']) ? $draft_item['children'] : null;
$draft_item['price'] = isset($draft_item['price']) ? $draft_item['price'] : null;
$draft_item['pos'] = $i++;
$draft_item['in_pdf'] = isset($draft_item['in_pdf']) ? true : false;
$BookingDraftItem->fill($draft_item);
$BookingDraftItem->save();
}
}
if($data['action'] === 'addItemDown'){
if ($data['action'] === 'addItemDown') {
$travel_program_id = null;
$request_date = null;
$comfort = 0;
if(count($booking->booking_draft_items)){
if (count($booking->booking_draft_items)) {
$first_booking_draft_item = $booking->booking_draft_items()->first();
$travel_program_id = $first_booking_draft_item->travel_program_id;
$request_date = $first_booking_draft_item->request_date;
$comfort = $first_booking_draft_item->comfort;
}
$booking->booking_draft_items()->create([
'booking_id' => $booking->id,
'travel_program_id' => $travel_program_id,
'fewo_lodging_id' => null,
'travel_class_id' => null,
'draft_item_id' => null,
'draft_type_id' => null,
'request_date' => $request_date,
'days_start' => null,
'days_duration' => null,
'start_date' => null,
'end_date' => null,
'service' => '',
'price_adult' => null,
'adult' => null ,
'price_children' => 0,
'children' => 0,
'price' => 0,
'pos' => $i,
'in_pdf' => true,
'comfort' => $comfort
]);
$add_draft_items_up_number = isset($data['add_draft_items_down_number']) ? intval($data['add_draft_items_down_number']) : 1;
for ($j = 0; $j < $add_draft_items_up_number; $j++) {
$booking->booking_draft_items()->create([
'booking_id' => $booking->id,
'travel_program_id' => $travel_program_id,
'fewo_lodging_id' => null,
'travel_class_id' => null,
'draft_item_id' => null,
'draft_type_id' => null,
'request_date' => $request_date,
'days_start' => null,
'days_duration' => null,
'start_date' => null,
'end_date' => null,
'service' => '',
'price_adult' => null,
'adult' => null,
'price_children' => 0,
'children' => 0,
'price' => 0,
'pos' => $i,
'in_pdf' => true,
'comfort' => $comfort
]);
$i++;
}
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
}
if (
$data['action'] === 'saveCustomer' || $data['action'] === 'saveAll' || $data['action'] === 'save_lead_status' || $data['action'] === 'update_booking' ||
$data['action'] === 'update_booking_services' || $data['action'] === 'update_booking_number' || $data['action'] === 'update_booking_price' ||
$data['action'] === 'update_service_provider_entry' || $data['action'] === 'update_booking_service_item' || $data['action'] === 'update_booking_participant'
) {
$customer = $this->custRepo->updateCustomerFromBooking($id, $data);
$booking = $this->bookingRepo->updateLeadStatus($id, $data);
$booking = $this->bookingRepo->updateBooking($id, $data);
$booking = $this->bookingRepo->updateBookingServices($id, $data);
$booking = $this->bookingRepo->updateBookingNumber($id, $data);
$booking = $this->bookingRepo->updateBookingPrice($id, $data);
$booking = $this->bookingRepo->updateServiceProviderEntry($id, $data);
$booking = $this->bookingRepo->updateBookingServiceItem($id, $data);
$booking = $this->bookingRepo->updateBookingParticipant($id, $data);
\Session()->flash('alert-save', '1');
switch ($data['action']) {
case 'saveCustomer':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingCustomer");
break;
case 'saveAll':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingOrganisation");
break;
case 'update_booking':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingBooking");
break;
case 'save_lead_status':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingLead");
break;
case 'update_booking_services':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingServices");
break;
case 'update_booking_number':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingMyJack");
break;
case 'update_booking_price':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingPrice");
break;
case 'update_service_provider_entry':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingProvider");
break;
case 'update_booking_service_item':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingCompany");
break;
case 'update_booking_participant':
$redirect = redirect(route('booking_detail', [$booking->id]) . "#collapseBookingParticipant");
break;
}
}
$booking->calculate_price_total();
if(strpos($data['action'], 'up_') !== false) {
if (strpos($data['action'], 'up_') !== false) {
$reId = intval(str_replace('up_', '', $data['action']));
$d_from = BookingDraftItem::findOrFail($reId);
$d_to = $booking->findBeforeDraftItemRelation($reId);
if($d_to) {
if ($d_to) {
$t_pos = $d_from->pos;
$d_from->pos = $d_to->pos;
$d_to->pos = $t_pos;
@ -233,11 +290,11 @@ class BookingController extends Controller
$d_to->save();
}
}
if(strpos($data['action'], 'down_') !== false) {
if (strpos($data['action'], 'down_') !== false) {
$reId = intval(str_replace('down_', '', $data['action']));
$d_from = BookingDraftItem::findOrFail($reId);
$d_to = $booking->findAfterDraftItemRelation($reId);
if($d_to) {
if ($d_to) {
$t_pos = $d_from->pos;
$d_from->pos = $d_to->pos;
$d_to->pos = $t_pos;
@ -246,14 +303,15 @@ class BookingController extends Controller
}
}
\Session()->flash('alert-save', '1');
return redirect(route('booking_detail', [$booking->id]));
return $redirect;
}
public function loadModal(){
$data = Request::all();
public function loadModal(Request $request)
{
$data = $request->all();
$ret = "";
if(Request::ajax()) {
if ($data['action'] === "new-customer-mail" || $data['action'] === "reply-customer-mail" || $data['action'] === "show-customer-mail" || $data['action'] === "edit-customer-mail"){
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'])) {
$tmp = [];
@ -266,86 +324,119 @@ class BookingController extends Controller
$ret = CustomerMailRepository::loadModal($data);
}
if($data['action'] === "modal-upload-booking-file") {
if ($data['action'] === "modal-upload-booking-file") {
$ret = view("booking.upload_modal", compact('data'))->render();
}
if($data['action'] === "edit_notice") {
if ($data['action'] === "edit_notice") {
$value = BookingNotice::findOrFail($data['id']);
$ret = view("booking.edit_notice_modal", compact('data', 'value'))->render();
}
if($data['action'] === "upload-booking-file"){
if($data['booking_id']){
if ($data['action'] === "createPDF_Coupon") {
$booking = Booking::findOrFail($data['booking_id']);
$data['has_coupon'] = $booking->hasDocument('coupon');
$data['default_value'] = config('booking.coupon_default_value');
$data['issue_date'] = date('d.m.Y');
$data['valid_date'] = \Carbon::now()->addMonths(config('booking.coupon_valid_date_month'))->format('d.m.Y');
$ret = view("booking.modal_create_coupon", compact('data'))->render();
}
if ($data['action'] === "createPDF_Storno") {
$booking = Booking::findOrFail($data['booking_id']);
$data['price'] = $booking->price;
if ($data['has_storno'] = $booking->hasDocument('storno')) {
$document_storno = $booking->getDocument('storno');
$data['storno_date'] = \Carbon::parse($document_storno->data->storno_date)->format('d.m.Y');
$data['storno_print'] = \Carbon::parse($document_storno->data->storno_print)->format('d.m.Y');
$data['storno_status_id'] = $document_storno->data->storno_status_id;
$data['storno_level'] = $document_storno->data->storno_level;
$data['storno_level_number'] = _number_format($document_storno->data->storno_level);
$data['storno_total_price'] = '';
} else {
$data['storno_date'] = date('d.m.Y');
$data['storno_print'] = date('d.m.Y');
$data['storno_status_id'] = null;
$data['storno_level'] = 100;
$data['storno_level_number'] = '';
$data['storno_total_price'] = '';
}
$ret = view("booking.modal_create_storno", compact('data'))->render();
}
if ($data['action'] === "upload-booking-file") {
if ($data['booking_id']) {
$bookingFileRepo = new BookingFileRepository(new BookingFile());
$bookingFileRepo->_set('disk', 'booking');
$bookingFileRepo->_set('booking_id', $data['booking_id']);
$bookingFileRepo->_set('dir', '/files/'.date('Y/m').'/');
$bookingFileRepo->_set('dir', '/files/' . date('Y/m') . '/');
$bookingFileRepo->_set('identifier', 'booking');
return $bookingFileRepo->uploadFile(Request::all());
return $bookingFileRepo->uploadFile($request->all());
}
}
}
return response()->json(['response' => $data, 'html'=>$ret]);
return response()->json(['response' => $data, 'html' => $ret]);
}
public function draftItemDelete($id){
$boking_draft_item = BookingDraftItem::findOrFail($id);
$booking = $boking_draft_item->booking;
$boking_draft_item->delete();
public function draftItemDelete($id)
{
$booking_draft_item = BookingDraftItem::findOrFail($id);
$booking = $booking_draft_item->booking;
$booking_draft_item->delete();
$booking->calculate_price_total();
\Session()->flash('alert-success', __('Eintrag gelöscht'));
return redirect(route('booking_detail', [$booking->id]));
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)){
if (!$booking = Booking::find($id)) {
abort(404);
}
if($action === 'change_travel_dates'){
$draftRepo = new DraftRepository($booking);
$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");
if ($action === 'change_travel_dates') {
$draftRepo = new DraftRepository($booking);
$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");
}
if($action === 'service_provider_entry_add_discount'){
$ServiceProvider = ServiceProvider::where('type', 'discount')->where('active',true)->first();
if ($action === 'service_provider_entry_add_discount') {
$ServiceProvider = ServiceProvider::where('type', 'discount')->where('active', true)->first();
ServiceProviderEntry::create([
'booking_id' => $booking->id,
'service_provider_id' => $ServiceProvider->id,
'type' => 'discount',
]);
\Session()->flash('alert-success', __('Leistungsträger neuer Rabatt hinzugefügt'));
return redirect(route('booking_detail', [$booking->id])."#collapseBookingProvider");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingProvider");
}
if($action === 'service_provider_entry_add_payment'){
$ServiceProvider = ServiceProvider::where('type', 'payment')->where('active',true)->first();
if ($action === 'service_provider_entry_add_payment') {
$ServiceProvider = ServiceProvider::where('type', 'payment')->where('active', true)->first();
ServiceProviderEntry::create([
'booking_id' => $booking->id,
'service_provider_id' => $ServiceProvider->id,
'type' => 'payment',
]);
\Session()->flash('alert-success', __('Leistungsträger neue Zahlung hinzugefügt'));
return redirect(route('booking_detail', [$booking->id])."#collapseBookingProvider");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingProvider");
}
if($action === 'booking_service_item_add'){
$TravelCompany = TravelCompany::where('active',true)->first();
if ($action === 'booking_service_item_add') {
$TravelCompany = TravelCompany::where('active', true)->first();
BookingServiceItem::create([
'booking_id' => $booking->id,
'travel_company_id' => $TravelCompany->id,
'travel_date' => now(),
]);
\Session()->flash('alert-success', __('Reiseveranstalter neue Leistung hinzugefügt'));
return redirect(route('booking_detail', [$booking->id])."#collapseBookingCompany");
}
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingCompany");
}
if($action === 'booking_participant_add'){
if ($action === 'booking_participant_add') {
Participant::create([
'booking_id' => $booking->id,
'nationality_id' => 1,
@ -353,75 +444,156 @@ class BookingController extends Controller
]);
\Session()->flash('alert-success', __('Neuen Teilnehmer hinzugefügt'));
return redirect(route('booking_detail', [$booking->id])."#collapseBookingParticipant");
}
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingParticipant");
}
}
public function delete($id, $del="booking"){
public function delete($id, $del = "booking")
{
if($del === 'booking') {
//$model = Booking::findOrFail($id);
//$model->delete();
\Session()->flash('alert-success', __('Löschen noch nicht programmiert'));
}
if($del === 'booking_file'){
if ($del === 'booking_file') {
$booking_file = BookingFile::findOrFail($id);
$booking = $booking_file->booking;
$fileRepo = new BookingFileRepository($booking_file);
$fileRepo->_set('disk', 'booking');
$fileRepo->delete();
$booking_file->delete();
$this->deleteBookingFile($booking_file);
\Session()->flash('alert-success', 'Datei gelöscht');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingFiles");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingFiles");
}
if($del === 'booking_notice'){
if ($del === 'booking_notice') {
$booking_notice = BookingNotice::findOrFail($id);
$booking = $booking_notice->booking;
$booking_notice->delete();
\Session()->flash('alert-success', 'Notiz gelöscht');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingNotice");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingNotice");
}
if($del === 'passolution_file'){
if ($del === 'passolution_file') {
$booking = Booking::findOrFail($id);
$booking->resyncPassolutionPDF();
\Session()->flash('alert-success', 'Passolution erneuert');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingFiles");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingFiles");
}
if($del === 'service_provider_entry'){
if ($del === 'service_provider_entry') {
$ServiceProviderEntry = ServiceProviderEntry::findOrFail($id);
$booking = $ServiceProviderEntry->booking;
$ServiceProviderEntry->delete();
\Session()->flash('alert-success', 'Leistungsträger gelöscht');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingProvider");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingProvider");
}
if($del === 'booking_service_item'){
if ($del === 'booking_service_item') {
$BookingServiceItem = BookingServiceItem::findOrFail($id);
$booking = $BookingServiceItem->booking;
$BookingServiceItem->delete();
\Session()->flash('alert-success', 'Reiseveranstalter gelöscht');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingCompany");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingCompany");
}
if($del === 'participant'){
if ($del === 'participant') {
$Participant = Participant::findOrFail($id);
$booking = $Participant->booking;
$Participant->delete();
\Session()->flash('alert-success', 'Teilnehmer gelöscht');
return redirect(route('booking_detail', [$booking->id])."#collapseBookingParticipant");
return redirect(route('booking_detail', [$booking->id]) . "#collapseBookingParticipant");
}
return redirect(route('requests'));
}
private function deleteBookingFile($booking_file)
{
$fileRepo = new BookingFileRepository($booking_file);
$fileRepo->_set('disk', 'booking');
$fileRepo->delete();
$booking_file->delete();
}
private function deleteCustomerFile($customer_file)
{
$fileRepo = new CustomerFileRepository($customer_file);
$fileRepo->_set('disk', 'customer');
$fileRepo->delete();
$customer_file->delete();
}
private function deleteBookingDocument($booking_document)
{
$booking_document->deleteFile();
$booking_document->delete();
}
public function deleteComplete($id)
{
$booking = Booking::findOrFail($id);
if ($booking->booking_files) {
foreach ($booking->booking_files as $booking_file) {
$this->deleteBookingFile($booking_file);
}
}
if ($booking->booking_notices) {
foreach ($booking->booking_notices as $booking_notice) {
$booking_notice->delete();
}
}
if ($booking->service_provider_entries) {
foreach ($booking->service_provider_entries as $service_provider_entry) {
$service_provider_entry->delete();
}
}
if ($booking->booking_service_items) {
foreach ($booking->booking_service_items as $booking_service_item) {
$booking_service_item->delete();
}
}
if ($booking->booking_provider_services) {
foreach ($booking->booking_provider_services as $booking_provider_service) {
$booking_provider_service->delete();
}
}
if ($booking->booking_country_services) {
foreach ($booking->booking_country_services as $booking_country_service) {
$booking_country_service->delete();
}
}
if ($booking->participants) {
foreach ($booking->participants as $participant) {
$participant->delete();
}
}
if ($booking->customer_mails) {
foreach ($booking->customer_mails_reverse as $customer_mail) {
if ($customer_mail->customer_files) {
foreach ($customer_mail->customer_files as $customer_file) {
$this->deleteCustomerFile($customer_file);
}
}
$customer_mail->delete();
}
}
if ($booking->booking_documents) {
foreach ($booking->booking_documents as $booking_document) {
if ($booking_document->identifier === 'coupon') {
//coupon set booking ID to null <- is need by the customer
$booking_document->booking_id = null;
$booking_document->save();
} else {
$this->deleteBookingDocument($booking_document);
}
}
}
if ($booking->coupons) {
foreach ($booking->coupons as $coupon) {
//coupon set booking ID to null <- is need by the customer
$coupon->booking_id = null;
$coupon->save();
}
}
$booking->delete();
\Session()->flash('alert-success', 'Buchung gelöscht');
return redirect(route('requests'));
}
}

View file

@ -5,9 +5,6 @@ namespace App\Http\Controllers\CMS;
use App\Http\Controllers\Controller;
use App\Models\CMSContent;
use App\Models\FewoLodging;
use App\Services\BookingFewo;
use App\Services\CreatePDF;
use App\Services\Util;
use Request;
@ -256,16 +253,6 @@ class CMSBookingController extends Controller
return redirect(route('cms_booking_content_detail', [$id]));
}
/* if($data['action'] === 'previewPDF'){
$pdf_content = BookingFewo::getFeWoCMSContentForPDF($this->identifier_content, $identifier_fewo);
$pdf_file = new CreatePDF('pdf.fewo_instructions');
return $pdf_file->create([
'contents' => $pdf_content,
'fewo' => $fewo
]
);
}
*/
\Session()->flash('alert-save', '1');
return redirect(route('cms_booking_content'));
}

View file

@ -62,6 +62,9 @@ class CMSContentController extends Controller
//store in cms old Datebase
\App\Models\Sym\CmsContent::create($data);
}else{
if($data['identifier'] === 'fewo-email-file'){
$data['integer'] = isset($data['default_travel_info']) ? 1 : 0;
}
$model = CMSContent::find($data['id']);
$model->fill($data);
$model->save();

View file

@ -8,7 +8,7 @@ use App\Models\CMSContent;
use App\Models\CMSInfo;
use App\Models\CMSInfoAvailable;
use App\Models\CMSInfoHoliday;
use App\Services\HTMLHelper;
use App\Helper\HTMLHelper;
use Request;
use Validator;

View file

@ -7,7 +7,7 @@ use App\Http\Controllers\Controller;
use App\Models\CMSContent;
use App\Models\FewoLodging;
use App\Services\BookingFewo;
use App\Services\CreatePDF;
use App\Libraries\CreatePDF;
use App\Services\Util;
use Request;

View file

@ -6,6 +6,7 @@ namespace App\Http\Controllers\CMS;
use App\Http\Controllers\Controller;
use App\Models\Feedback;
use Carbon\Carbon;
use IqContent\LaravelFilemanager\Lfm;
use Request;
class CMSFeedbackController extends Controller
@ -38,6 +39,8 @@ class CMSFeedbackController extends Controller
$feedback = new Feedback();
$id = 'new';
$feedback->status = 1;
$feedback->content_new = "";
}else{
$feedback = Feedback::findOrFail($id);
@ -46,6 +49,7 @@ class CMSFeedbackController extends Controller
$data = [
'feedback' => $feedback,
'id' => $id,
'lfm_helper' => app(Lfm::class),
];
return view('cms.feedback.detail', $data);
@ -67,15 +71,18 @@ class CMSFeedbackController extends Controller
$feedback->show_in_navi = 1;
$feedback->catalog_id = 1;
}else{
$feedback = Feedback::findOrFail($id);
}
$feedback->title = $data['title'];
$feedback->status = isset($data['status']) ? true : false;
$feedback->slug = $data['slug'];
$feedback->date = $data['date'];
$feedback->content = $data['content'];
$feedback->content_new = $data['content_new'];
$feedback->box_body = $data['image'];
$feedback->description = $data['description'];
$feedback->pagetitle = $data['pagetitle'];
$feedback->keywords = $data['keywords'];
@ -93,6 +100,9 @@ class CMSFeedbackController extends Controller
if($first_feedback = $parent_feedback->children->first()){
$feedback->lft = $first_feedback->lft;
$feedback->rgt = $first_feedback->rgt;
}else{
$feedback->lft = $parent_feedback->lft +1;
$feedback->rgt = $parent_feedback->lft +2;
}
$feedback->tree_root = $parent_feedback->tree_root;

View file

@ -0,0 +1,118 @@
<?php
namespace App\Http\Controllers\CMS;
use App\Http\Controllers\Controller;
use App\Models\News;
use App\Models\Page;
use Carbon\Carbon;
use IqContent\LaravelFilemanager\Lfm;
use Illuminate\Support\Str;
use Request;
class CMSNewsController extends Controller
{
/*
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware(['admin', '2fa']);
}
public function index()
{
$data = [
'news' => News::all(), //News::where('lvl', 1)->get(),
];
return view('cms.news.index', $data);
}
public function detail($id)
{
if ($id === "new") {
$news = new News();
$id = 'new';
$news->status = 1;
$news->content_new = "";
} else {
$news = News::findOrFail($id);
$id = $news->id;
}
$data = [
'news' => $news,
'id' => $id,
'lfm_helper' => app(Lfm::class),
];
return view('cms.news.detail', $data);
}
public function store($id)
{
$data = Request::all();
if ($id === "new") {
$news = new News();
$news->model = 'news';
$news->owner_second = 0;
$news->show_in_navi = 1;
$news->catalog_id = 1;
} else {
$news = News::findOrFail($id);
}
$news->title = $data['title'];
$news->status = isset($data['status']) ? true : false;
$news->slug = Str::slug($data['slug']);
$news->date = $data['date'];
$news->content_new = $data['content_new'];
$news->box_body = $data['image'];
$news->description = $data['description'];
$news->pagetitle = $data['pagetitle'];
$news->keywords = $data['keywords'];
$news->order = (new Carbon($news->date))->format('Ymd') * -1;
$root_news = News::where('cms_settings', 'news_root')->first();
if ($id != $root_news->id) {
//root ID = 3126
$news->lvl = 1;
$news->owner = $root_news->id;
$news->parent_id = $root_news->id;
$news->tree_root = $root_news->id;
if ($first_news = $root_news->children->first()) {
$news->lft = $first_news->lft;
$news->rgt = $first_news->rgt;
} else {
$news->lft = $root_news->lft + 1;
$news->rgt = $root_news->lft + 2;
}
}
$news->save();
\Session()->flash('alert-save', '1');
return redirect(route('cms_news_detail', [$news->id]));
}
public function delete($id)
{
$news = News::findOrFail($id);
//TODO
//check for delete, only delete lvl 2 .,...?
if ($news->lvl != 1) {
abort(404);
die();
}
$news->delete();
\Session()->flash('alert-success', __('News gelöscht'));
return redirect(route('cms_news'));
}
}

View file

@ -54,6 +54,15 @@ class CMSTravelGuideController extends Controller
}
public function test()
{
//make tree
$data = [
];
return view('cms.travel_guide.test', $data);
}
public function detail($id)
{
if($id === "new") {

View file

@ -0,0 +1,312 @@
<?php
namespace App\Http\Controllers;
use App\Models\Contact;
use App\Repositories\ContactRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Yajra\DataTables\Facades\DataTables;
class ContactController extends Controller
{
public function __construct(private readonly ContactRepository $contactRepo)
{
$this->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 '<a href="' . route('contact_detail', [$contact->id]) . '" class="btn icon-btn btn-sm btn-primary" title="Öffnen"><span class="fa fa-edit"></span></a>';
})
->addColumn('action_delete', function (Contact $contact) use ($showDeleted) {
if ($showDeleted) {
return '<button class="btn icon-btn btn-xs btn-success ml-1 btn-contact-restore" '
. 'data-id="' . $contact->id . '" '
. 'data-name="' . e($contact->fullName()) . '" '
. 'title="Wiederherstellen"><span class="fa fa-undo"></span></button>';
}
return '<button class="btn icon-btn btn-xs btn-danger ml-1 btn-contact-delete" '
. 'data-id="' . $contact->id . '" '
. 'data-name="' . e($contact->fullName()) . '" '
. 'title="Löschen"><span class="fa fa-trash"></span></button>';
})
->addColumn('id', function (Contact $contact) use ($showDeleted) {
if ($showDeleted) {
return '<span data-order="' . $contact->id . '">' . $contact->id . '</span>';
}
return '<a data-order="' . $contact->id . '" href="' . route('contact_detail', [$contact->id]) . '">' . $contact->id . '</a>';
})
->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);
}
}

View file

@ -47,10 +47,14 @@ class CustomerController extends Controller
public function store($id)
{
$data = Request::all();
if(!isset($data['action'])){
abort(403, 'keine Action');
}
//save
$customer = $this->custRepo->updateCustomer($id, $data);
\Session()->flash('alert-save', '1');
if($data['action'] === 'saveCustomer'){
$customer = $this->custRepo->updateCustomer($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('customer_detail', [$id]).'#collapseCustomerDetail');
}
return back();
@ -64,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) {

View file

@ -70,7 +70,7 @@ class CustomerFewoMailController extends Controller
$customer_mail->save();
}
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function delete($id){
@ -128,14 +128,14 @@ class CustomerFewoMailController extends Controller
}else{
\Session()->flash('alert-success', "Mail gesendet!");
}
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function replyMail(CustomerFewoMailRepository $customerFewoMailRepository){
$data = Request::all();
$customerFewoMailRepository->replyStore($data);
\Session()->flash('alert-success', "Mail gespeichert!");
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function getEmailTemplates()

View file

@ -3,24 +3,25 @@
namespace App\Http\Controllers;
use App\Models\BookingApplication;
use App\Models\BookingConfirmation;
use App\Models\BookingStorno;
use App\Models\BookingVoucher;
use App\Models\Coupon;
use App\Models\FewoLodging;
use App\Models\InsuranceCertificate;
use App\Models\TravelInsurance;
use App\Repositories\CustomerFileRepository;
use App\Services\BookingFewo;
use App\Services\CreateCouponPDF;
use App\Services\CreatePDF;
use App\Services\Util;
use Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\URL;
use Request;
use Response;
use App\Models\Coupon;
use App\Services\Util;
use App\Models\FewoLodging;
use App\Libraries\CreatePDF;
use App\Models\BookingStorno;
use App\Services\BookingFewo;
use App\Models\BookingVoucher;
use App\Models\TravelInsurance;
use App\Libraries\CreateCouponPDF;
use App\Models\BookingApplication;
use App\Models\BookingConfirmation;
use Illuminate\Support\Facades\URL;
use App\Models\BookingVoucherAgency;
use App\Models\InsuranceCertificate;
use App\Repositories\CustomerFileRepository;
use Illuminate\Database\Eloquent\Collection;
class CustomerFileController extends Controller
{
@ -45,20 +46,21 @@ class CustomerFileController extends Controller
$content_disposition = $cd ? 'attachment' : 'inline';
$file = false;
$filename = "";
switch ($model){
case 'booking_application':
case 'registration':
if($booking_application = BookingApplication::find($id)){
$filename = "Buchungsauftrag-".$booking_application->booking->getBookingNumber().".pdf";
$file = base64_decode($booking_application->binary_data);
}
break;
case 'booking_confirmation':
case 'confirmation':
if($booking_confirmation = BookingConfirmation::find($id)){
$filename = "Reisebestaetigung-".$booking_confirmation->booking->getBookingNumber().".pdf";
$file = base64_decode($booking_confirmation->binary_data);
}
break;
case 'booking_storno':
case 'storno':
if($booking_stornos = BookingStorno::find($id)){
$filename = "Reisestornierung-".$booking_stornos->booking->getBookingNumber().".pdf";
$file = base64_decode($booking_stornos->binary_data);
@ -67,17 +69,23 @@ class CustomerFileController extends Controller
case 'coupon':
if($coupon = Coupon::find($id)){
$filename = "Gutschein-".$coupon->number.".pdf";
$pdf = new CreateCouponPDF($coupon);
$pdf->create();
return $pdf->output($filename, $cd);
}
break;
case 'booking_voucher':
if($booking_vouchers = BookingVoucher::find($id)){
$filename = "Voucher-".$booking_vouchers->booking->getBookingNumber().".pdf";
$file = base64_decode($booking_vouchers->binary_data);
case 'voucher':
if($booking_voucher = BookingVoucher::find($id)){
$filename = "Voucher-".$booking_voucher->booking->getBookingNumber().".pdf";
$file = base64_decode($booking_voucher->binary_data);
}
break;
case 'voucher_agency':
if($booking_voucher_agency = BookingVoucherAgency::find($id)){
$filename = "Voucher-Agentur-".$booking_voucher_agency->booking->getBookingNumber().".pdf";
$file = base64_decode($booking_voucher_agency->binary_data);
}
break;
case 'insurance_certificate':

View file

@ -91,7 +91,7 @@ class CustomerMailController extends Controller
}
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function delete($id){
@ -148,14 +148,14 @@ class CustomerMailController extends Controller
}else{
\Session()->flash('alert-success', "Mail gesendet!");
}
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function replyMail(CustomerMailRepository $customerMailRepository){
$data = Request::all();
$customerMailRepository->replyStore($data);
\Session()->flash('alert-success', "Mail gespeichert!");
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function getEmailTemplates()

View file

@ -3,7 +3,7 @@
namespace App\Http\Controllers;
use App\Models\Booking;
use App\Services\HTMLHelper;
use App\Helper\HTMLHelper;
use Carbon\Carbon;
use Composer\DependencyResolver\Request;
use DataTables;

View file

@ -13,67 +13,84 @@ class FileController extends Controller
*
* @return void
*/
public function __construct()
{
}
public function __construct() {}
public function show($id = null, $disk = null, $do='file')
public function show($id = null, $disk = null, $do = 'file')
{
$path = "";
$filename = "";
if ($disk === 'customer'){
$file = \App\Models\CustomerFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
}
if ($disk === 'travel_user'){
$file = \App\Models\CustomerFewoFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
switch ($disk) {
case 'customer':
$file = \App\Models\CustomerFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
break;
case 'travel_user':
$file = \App\Models\CustomerFewoFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
break;
case 'booking':
$file = \App\Models\BookingFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
break;
case 'general':
$file = \App\Models\GeneralFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
break;
case 'booking_fewo':
$file = \App\Models\TravelUserBookingFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
break;
case 'lead':
$file = \App\Models\LeadFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
break;
case 'cms_file':
$file = \App\Models\CMSContent::findOrFail($id);
$filename = $file->name;
$path = $file->getPath();
break;
case 'booking_document':
$file = \App\Models\BookingDocument::findOrFail($id);
$filename = $file->name;
$path = $file->getPath();
break;
}
if ($disk === 'booking'){
$file = \App\Models\BookingFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
}
if ($disk === 'general'){
$file = \App\Models\GeneralFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
}
if ($disk === 'booking_fewo'){
$file = \App\Models\TravelUserBookingFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
}
if ($disk === 'lead'){
$file = \App\Models\LeadFile::findOrFail($id);
$filename = $file->original_name;
$path = $file->getPath();
}
if ($disk === 'cms_file'){
$file = \App\Models\CMSContent::findOrFail($id);
$filename = $file->name;
$path = $file->getPath();
}
if (file_exists($path)) {
if($do === "download"){
return Response::download($path, $filename);
// Cache-Control Header für PDFs und andere Dateien, die sich häufig ändern
$headers = [
'Cache-Control' => 'no-cache, no-store, must-revalidate',
'Pragma' => 'no-cache',
'Expires' => '0'
];
switch ($do) {
case 'file':
return Response::file($path, $headers);
break;
case 'download':
return Response::download($path, $filename, $headers);
break;
case 'url':
return $path . $filename;
break;
}
return Response::file($path);
}
}
public function showExpert($type = null, $class = null, $year = null, $file = null, $do = null) {
public function showExpert($type = null, $class = null, $year = null, $file = null, $do = null)
{
/*if ($type == 'xls') {
$path = storage_path("app/export/");
@ -81,35 +98,39 @@ class FileController extends Controller
}*/
$path = "";
$filename = "";
$headers = [];
$headers = [
'Cache-Control' => 'no-cache, no-store, must-revalidate',
'Pragma' => 'no-cache',
'Expires' => '0'
];
if ($class === 'invoices' || $class === 'infos'){
if ($class === 'invoices' || $class === 'infos') {
$headers = [
'Content-Type: application/pdf',
'Pragma: no-cache',
'Cache-Control: no-store,no-cache, must-revalidate, post-check=0, pre-check=0'
];
$dir = $year."/";
$dir = $year . "/";
$filename = $file;
if ($type === 'fewo') {
if(Storage::disk('fewo_invoices')->exists( $dir.$filename )){
$path = Storage::disk('fewo_invoices')->path($dir.$filename);
if (Storage::disk('fewo_invoices')->exists($dir . $filename)) {
$path = Storage::disk('fewo_invoices')->path($dir . $filename);
}
}
if ($type === 'fewo') {
if(Storage::disk('fewo_infos')->exists( $dir.$filename )){
$path = Storage::disk('fewo_infos')->path($dir.$filename);
if (Storage::disk('fewo_infos')->exists($dir . $filename)) {
$path = Storage::disk('fewo_infos')->path($dir . $filename);
}
}
}
if (file_exists($path)) {
if($do === "download"){
if ($do === "download") {
return Response::download($path, $filename, $headers);
}
if($do === "file"){
if ($do === "file") {
return Response::file($path, $headers);
}
}
}
}
}

View file

@ -14,15 +14,6 @@ use Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
}
/**
* Show the application dashboard.
*

View file

@ -202,7 +202,6 @@ class ContentTreeController extends Controller
$data['user_id'] = \Auth::user()->id;
$data['active'] = isset($data['active']) ? true : false;
$data['search'] = isset($data['search']) ? true : false;
if (isset($data['action'])) {
switch ($data['action']) {
case 'tree-content' :

View file

@ -1,31 +0,0 @@
<?php
namespace App\Http\Controllers\IQ;
use App\Http\Controllers\Controller;
use Request;
use IqContent\LaravelFilemanager\Controllers\LfmController;
class ContentAssetController extends LfmController
{
public function index()
{
$data = [
'models' => [],
'lfm_helper' => $this->helper,
'modal' => false,
];
return view('iq.content.assets.index', $data);
}
public function modal(){
$data = [
'models' => [],
'lfm_helper' => $this->helper,
'modal' => true,
];
return view('iq.content.assets.body', $data);
}
}

View file

@ -2,17 +2,19 @@
namespace App\Http\Controllers;
use Carbon;
use Request;
use App\Models\Lead;
use App\Models\LeadFile;
use App\Models\LeadMail;
use App\Models\LeadNotice;
use App\Models\StatusHistory;
use App\Models\LeadParticipant;
use App\Repositories\LeadRepository;
use App\Models\StatusHistory;
use App\Repositories\CustomerRepository;
use App\Repositories\LeadFileRepository;
use App\Repositories\LeadRepository;
use App\Services\Util;
use Carbon;
use Request;
use Session;
class LeadController extends Controller
{
@ -38,6 +40,7 @@ class LeadController extends Controller
public function detail($id)
{
if($id === "new") {
$lead = new Lead();
$id = 'new';
@ -47,6 +50,7 @@ class LeadController extends Controller
$lead->getPassolutionPDF(true);
$id = $lead->id;
}
$data = [
'lead' => $lead,
'id' => $id,
@ -59,19 +63,34 @@ class LeadController extends Controller
public function store($id)
{
$data = Request::all();
if(!isset($data['action'])){
abort(403, 'keine Action');
}
//save
if($data['action'] === 'createBooking'){
$lead = $this->leadRepo->createBooking($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('lead_detail', [$id]).'#collapseLeadBooking');
}
if($data['action'] === 'saveCustomer'){
if($data['action'] === 'saveCustomer' || $data['action'] === 'saveLead' || $data['action'] === 'saveStatus' || $data['action'] === 'update_lead_participant'){
//@dd($data);
$customer = $this->custRepo->updateCustomerFromLead($id, $data);
\Session()->flash('alert-save', '1');
$lead = $this->leadRepo->updateLead($id, $data);
$lead = $this->leadRepo->updateLeadParticipant($id, $data);
}
if($data['action'] === 'saveCustomer'){
return redirect(route('lead_detail', [$id]).'#collapseLeadCustomer');
}
if($data['action'] === 'saveLead'){
$lead = $this->leadRepo->updateLead($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('lead_detail', [$id]).'#collapseLeadDetail');
}
if($data['action'] === 'update_lead_participant'){
return redirect(route('lead_detail', [$lead->id])."#collapseBookingParticipant");
}
if($data['action'] === 'saveStatus'){
$lead = $this->leadRepo->updateLeadStatus($id, $data);
\Session()->flash('alert-save', '1');
@ -88,11 +107,7 @@ class LeadController extends Controller
\Session()->flash('alert-save', '1');
return redirect(route('lead_detail', [$lead->id])."#collapseLeadNotice");
}
if($data['action'] === 'update_lead_participant'){
$lead = $this->leadRepo->updateLeadParticipant($id, $data);
\Session()->flash('alert-save', '1');
return redirect(route('lead_detail', [$lead->id])."#collapseBookingParticipant");
}
return back();
@ -165,6 +180,11 @@ class LeadController extends Controller
if($del === 'lead') {
$lead = Lead::findOrFail($id);
if($lead->bookings->count()){
\Session()->flash('alert-error', 'Kann nicht gelöscht werden, die Anfrage hat Buchungen');
return redirect(route('lead_detail', [$lead->id])."#collapseLeadBooking");
}
//Files
$leadFiles = LeadFile::where('lead_id', $lead->id)->get();
foreach ($leadFiles as $leadFile) {
@ -212,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) {
@ -227,6 +247,10 @@ class LeadController extends Controller
->addColumn('request_date', function (Lead $lead) {
return Carbon::parse($lead->request_date)->format(\Util::formatDateDB());
})
->addColumn('travel_country', function (Lead $lead) {
return $lead->getTravelCountryDestco();
})
->addColumn('status', function (Lead $lead) {
return $lead->getStatusBadge();
})
@ -262,6 +286,7 @@ class LeadController extends Controller
})
->orderColumn('id', 'id $1')
->orderColumn('customer_id', 'customer_id $1')
->orderColumn('request_date', 'request_date $1')
->orderColumn('status', 'status_id $1')
->orderColumn('last_lead_email', function ($query, $order) {
@ -271,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);
@ -287,7 +312,7 @@ class LeadController extends Controller
$query->where('customer_id', 'LIKE', '%'.$keyword.'%');
}
})
->rawColumns(['action_edit', 'customer_id', 'sf_guard_user_id', 'id', 'status', 'last_lead_email', 'lead_notice', 'action_delete'])
->rawColumns(['action_edit', 'customer_id', 'sf_guard_user_id', 'id', 'status', 'last_lead_email', 'travel_country', 'lead_notice', 'action_delete'])
->make(true);
}

View file

@ -54,7 +54,7 @@ class LeadMailController extends Controller
\Session()->flash('alert-success', __('E-Mail weitergeleitet'));
}
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function delete($id){
@ -110,14 +110,14 @@ class LeadMailController extends Controller
}else{
\Session()->flash('alert-success', "Mail gesendet!");
}
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}
public function replyMail(LeadMailRepository $LeadMailRepository){
$data = Request::all();
$LeadMailRepository->replyStore($data);
\Session()->flash('alert-success', "Mail gespeichert!");
return back();
return back()->with('collapse_shows', $data['collapse_shows'] ? $data['collapse_shows'] : []);
}

View file

@ -0,0 +1,165 @@
<?php
namespace App\Http\Controllers;
use App\Services\NavigationTreeService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class NavigationTreeController extends Controller
{
protected $navigationService;
public function __construct(NavigationTreeService $navigationService)
{
$this->navigationService = $navigationService;
}
/**
* Zeigt die Navigationsbaum-Übersicht
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('navigation.index');
}
/**
* Gibt die Navigationsbaum-Daten als JSON zurück (Frontend-Struktur)
*
* @param Request $request
* @return JsonResponse
*/
public function getData(Request $request): JsonResponse
{
try {
$includeHidden = $request->get('include_hidden', true);
$tree = $this->navigationService->getFrontendNavigationTree($includeHidden);
return response()->json([
'success' => true,
'data' => $tree,
'meta' => [
'total_nodes' => $this->navigationService->countNodes($tree),
'include_hidden' => $includeHidden,
'structure' => 'frontend'
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
/**
* Sucht im Navigationsbaum
*
* @param Request $request
* @return JsonResponse
*/
public function search(Request $request): JsonResponse
{
try {
$query = $request->get('query', '');
$flatList = $this->navigationService->getFlatNavigationList();
// Filtere nach Suchbegriff
$results = array_filter($flatList, function ($node) use ($query) {
return stripos($node['title'], $query) !== false
|| stripos($node['slug'], $query) !== false
|| stripos($node['url'], $query) !== false;
});
return response()->json([
'success' => true,
'data' => array_values($results),
'meta' => [
'query' => $query,
'total_results' => count($results)
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
/**
* Exportiert den Navigationsbaum als JSON-Datei
*
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function export(Request $request)
{
try {
$includeHidden = $request->get('include_hidden', true);
$tree = $this->navigationService->getFrontendNavigationTree($includeHidden);
$filename = 'navigation-tree-frontend-' . date('Y-m-d-His') . '.json';
$json = json_encode($tree, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return response($json)
->header('Content-Type', 'application/json')
->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
} catch (\Exception $e) {
return back()->with('error', 'Export fehlgeschlagen: ' . $e->getMessage());
}
}
/**
* Löscht den Cache
*
* @return \Illuminate\Http\RedirectResponse
*/
public function clearCache()
{
try {
$this->navigationService->clearCache();
return back()->with('success', 'Navigation-Cache erfolgreich gelöscht');
} catch (\Exception $e) {
return back()->with('error', 'Cache-Löschung fehlgeschlagen: ' . $e->getMessage());
}
}
/**
* Zeigt die Statistiken
*
* @return JsonResponse
*/
public function stats(): JsonResponse
{
try {
$allTree = $this->navigationService->getNavigationTree(false);
$activeTree = $this->navigationService->getNavigationTree(true);
$flatList = $this->navigationService->getFlatNavigationList();
// Zähle verschiedene Typen
$stats = [
'total_pages' => count($flatList),
'total_nodes' => $this->navigationService->countNodes($allTree),
'active_nodes' => $this->navigationService->countNodes($activeTree),
'inactive_nodes' => $this->navigationService->countNodes($allTree) - $this->navigationService->countNodes($activeTree),
'travel_programs' => count(array_filter($flatList, fn($n) => $n['is_travel_program'])),
'fewo_lodgings' => count(array_filter($flatList, fn($n) => $n['is_fewo_lodging'])),
'country_pages' => count(array_filter($flatList, fn($n) => $n['is_country_page'])),
];
return response()->json([
'success' => true,
'data' => $stats
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage()
], 500);
}
}
}

View file

@ -0,0 +1,391 @@
<?php
namespace App\Http\Controllers;
use App\Models\NewsletterContact;
use App\Models\NewsletterLog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Validator;
use Yajra\DataTables\Facades\DataTables;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\NewsletterExport;
use Carbon\Carbon;
class NewsletterController extends Controller
{
public function __construct()
{
$this->middleware(['admin', '2fa']);
}
/**
* Liste aller Newsletter-Kontakte
*/
public function index()
{
$data = [
'statistics' => $this->getStatistics(),
];
return view('newsletter.index', $data);
}
/**
* DataTables Daten für die Liste
*/
public function getDatatable(Request $request)
{
$query = NewsletterContact::query()
->select([
'id',
'email',
'firstname',
'lastname',
'group_kulturreisen',
'group_ferienwohnungen',
'status',
'source',
'total_bookings_kulturreisen',
'total_bookings_ferienwohnungen',
'last_booking_at',
'last_travel_end_date',
'created_at',
]);
// Filter nach Gruppe
if ($request->has('group') && $request->group != '') {
if ($request->group == 'kulturreisen') {
$query->where('group_kulturreisen', true);
} elseif ($request->group == 'ferienwohnungen') {
$query->where('group_ferienwohnungen', true);
}
}
// Filter nach Status
if ($request->has('status') && $request->status != '') {
$query->where('status', $request->status);
}
// Filter nach Source
if ($request->has('source') && $request->source != '') {
$query->where('source', $request->source);
}
// Filter nach Datum der letzten Buchung (von)
if ($request->has('travel_from') && $request->travel_from != '') {
$query->whereDate('last_travel_end_date', '>=', Carbon::parse($request->travel_from)->format('Y-m-d H:i:s'));
}
// Filter nach Datum der letzten Buchung (bis)
if ($request->has('travel_to') && $request->travel_to != '') {
$query->whereDate('last_travel_end_date', '<=', Carbon::parse($request->travel_to)->format('Y-m-d H:i:s'));
}
return DataTables::of($query)
->addColumn('full_name', function ($contact) {
return $contact->full_name ?: '-';
})
->addColumn('groups', function ($contact) {
$html = '';
if ($contact->group_kulturreisen) {
$html .= '<span class="badge badge-info">Kulturreisen</span> ';
}
if ($contact->group_ferienwohnungen) {
$html .= '<span class="badge badge-primary">Ferienwohnungen</span>';
}
return $html ?: '-';
})
->addColumn('status_badge', function ($contact) {
return '<span class="badge badge-' . $contact->status_color . '">' . $contact->status_label . '</span>';
})
->addColumn('total_bookings', function ($contact) {
$html = '';
if ($contact->total_bookings_kulturreisen > 0) {
$html .= '<span class="badge badge-secondary">K: ' . $contact->total_bookings_kulturreisen . '</span> ';
}
if ($contact->total_bookings_ferienwohnungen > 0) {
$html .= '<span class="badge badge-secondary">F: ' . $contact->total_bookings_ferienwohnungen . '</span>';
}
return $html ?: '0';
})
->addColumn('last_booking', function ($contact) {
return $contact->last_booking_at ? $contact->last_booking_at->format('d.m.Y') : '-';
})
->addColumn('last_travel', function ($contact) {
return $contact->last_travel_end_date ? $contact->last_travel_end_date->format('d.m.Y') : '-';
})
->addColumn('source_label', function ($contact) {
return $contact->source_label;
})
->addColumn('created', function ($contact) {
return $contact->created_at->format('d.m.Y');
})
->addColumn('actions', function ($contact) {
$html = '<div class="btn-group">';
$html .= '<a href="' . route('newsletter.detail', $contact->id) . '" class="btn btn-sm btn-info"><i class="fa fa-eye"></i></a>';
$html .= '<a href="' . route('newsletter.edit', $contact->id) . '" class="btn btn-sm btn-primary"><i class="fa fa-edit"></i></a>';
$html .= '</div>';
return $html;
})
->rawColumns(['groups', 'status_badge', 'total_bookings', 'actions'])
->make(true);
}
/**
* Detailansicht eines Kontakts
*/
public function detail($id)
{
$contact = NewsletterContact::with(['customer', 'travel_user', 'logs.user'])
->findOrFail($id);
$data = [
'contact' => $contact,
];
return view('newsletter.detail', $data);
}
/**
* Formular zum Bearbeiten eines Kontakts
*/
public function edit($id)
{
if ($id === 'new') {
$contact = new NewsletterContact();
$contact->status = NewsletterContact::STATUS_ACTIVE;
$contact->source = NewsletterContact::SOURCE_MANUAL;
} else {
$contact = NewsletterContact::findOrFail($id);
}
$data = [
'contact' => $contact,
'id' => $id,
];
return view('newsletter.edit', $data);
}
/**
* Speichern eines Kontakts
*/
public function store($id, Request $request)
{
$rules = [
'email' => 'required|email',
'status' => 'required|in:' . implode(',', [
NewsletterContact::STATUS_ACTIVE,
NewsletterContact::STATUS_INACTIVE,
NewsletterContact::STATUS_UNSUBSCRIBED,
NewsletterContact::STATUS_BOUNCED,
]),
];
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
return back()
->withErrors($validator)
->withInput();
}
if ($id === 'new') {
$contact = new NewsletterContact();
$isNew = true;
} else {
$contact = NewsletterContact::findOrFail($id);
$isNew = false;
}
// Speichere alte Werte für Log
$oldStatus = $contact->status;
$oldGroups = [
'kulturreisen' => $contact->group_kulturreisen,
'ferienwohnungen' => $contact->group_ferienwohnungen,
];
$contact->email = strtolower(trim($request->email));
$contact->firstname = $request->firstname;
$contact->lastname = $request->lastname;
$contact->status = $request->status;
$contact->source = $request->source ?? NewsletterContact::SOURCE_MANUAL;
$contact->group_kulturreisen = $request->has('group_kulturreisen');
$contact->group_ferienwohnungen = $request->has('group_ferienwohnungen');
$contact->notes = $request->notes;
if ($isNew) {
$contact->subscribed_at = now();
}
$contact->save();
// Log erstellen
if ($isNew) {
$contact->logs()->create([
'action' => 'subscribed',
'description' => 'Kontakt manuell erstellt',
'user_id' => auth()->id(),
]);
} else {
// Status geändert?
if ($oldStatus !== $contact->status) {
$contact->logs()->create([
'action' => 'status_changed',
'description' => 'Status geändert von ' . NewsletterContact::$statusLabels[$oldStatus] . ' zu ' . $contact->status_label,
'user_id' => auth()->id(),
]);
}
// Gruppen geändert?
if (
$oldGroups['kulturreisen'] !== $contact->group_kulturreisen ||
$oldGroups['ferienwohnungen'] !== $contact->group_ferienwohnungen
) {
$contact->logs()->create([
'action' => 'group_changed',
'description' => 'Gruppenzugehörigkeit geändert',
'user_id' => auth()->id(),
]);
}
}
\Session()->flash('alert-success', $isNew ? 'Kontakt erstellt' : 'Kontakt aktualisiert');
return redirect()->route('newsletter.detail', $contact->id);
}
/**
* Kontakt löschen (soft delete)
*/
public function delete($id)
{
$contact = NewsletterContact::findOrFail($id);
$contact->logs()->create([
'action' => 'unsubscribed',
'description' => 'Kontakt gelöscht',
'user_id' => auth()->id(),
]);
$contact->delete();
\Session()->flash('alert-success', 'Kontakt gelöscht');
return redirect()->route('newsletter.index');
}
/**
* Kontakt abmelden
*/
public function unsubscribe($id, Request $request)
{
$contact = NewsletterContact::findOrFail($id);
$contact->unsubscribe($request->reason);
\Session()->flash('alert-success', 'Kontakt abgemeldet');
return back();
}
/**
* Kontakt wieder aktivieren
*/
public function resubscribe($id)
{
$contact = NewsletterContact::findOrFail($id);
$contact->resubscribe();
\Session()->flash('alert-success', 'Kontakt wieder aktiviert');
return back();
}
/**
* Synchronisation starten
*/
public function sync(Request $request)
{
$type = $request->get('type', 'all');
$force = $request->has('force');
$output = [];
if ($type === 'all' || $type === 'kulturreisen') {
Artisan::call('newsletter:sync-kulturreisen', $force ? ['--force' => true] : []);
$output['kulturreisen'] = Artisan::output();
}
if ($type === 'all' || $type === 'ferienwohnungen') {
Artisan::call('newsletter:sync-ferienwohnungen', $force ? ['--force' => true] : []);
$output['ferienwohnungen'] = Artisan::output();
}
\Session()->flash('alert-success', 'Synchronisation abgeschlossen');
return back()->with('sync_output', $output);
}
/**
* Export von Kontakten
*/
public function export(Request $request)
{
$query = NewsletterContact::query();
// Filter nach Gruppe
if ($request->has('group') && $request->group != '') {
if ($request->group == 'kulturreisen') {
$query->where('group_kulturreisen', true);
} elseif ($request->group == 'ferienwohnungen') {
$query->where('group_ferienwohnungen', true);
}
}
// Filter nach Status
if ($request->has('status') && $request->status != '') {
$query->where('status', $request->status);
}
// Filter nach Source
if ($request->has('source') && $request->source != '') {
$query->where('source', $request->source);
}
// Filter nach Datum der letzten Reise (von)
if ($request->has('travel_from') && $request->travel_from != '') {
$query->whereDate('last_travel_end_date', '>=', Carbon::parse($request->travel_from)->format('Y-m-d H:i:s'));
}
// Filter nach Datum der letzten Reise (bis)
if ($request->has('travel_to') && $request->travel_to != '') {
$query->whereDate('last_travel_end_date', '<=', Carbon::parse($request->travel_to)->format('Y-m-d H:i:s'));
}
$contacts = $query->get();
// Dateiname mit Datum und Filter-Infos
$group = $request->get('group', 'all');
$status = $request->get('status', 'all');
$filename = 'newsletter_' . $group . '_' . $status . '_' . date('Y-m-d') . '.csv';
return Excel::download(new NewsletterExport($contacts), $filename);
}
/**
* Statistiken für Dashboard
*/
private function getStatistics()
{
return [
'total' => NewsletterContact::count(),
'active' => NewsletterContact::where('status', NewsletterContact::STATUS_ACTIVE)->count(),
'kulturreisen' => NewsletterContact::where('group_kulturreisen', true)->count(),
'ferienwohnungen' => NewsletterContact::where('group_ferienwohnungen', true)->count(),
'with_bookings' => NewsletterContact::withBookings()->count(),
'multiple_bookers' => NewsletterContact::multipleBookers()->count(),
'unsubscribed' => NewsletterContact::where('status', NewsletterContact::STATUS_UNSUBSCRIBED)->count(),
'last_sync' => NewsletterContact::max('last_synced_at'),
];
}
}

View file

@ -2,19 +2,19 @@
namespace App\Http\Controllers;
use Request;
use DataTables;
use Carbon\Carbon;
use App\Models\Status;
use App\Models\Airline;
use App\Models\Airport;
use App\Models\Booking;
use App\Models\SfGuardUser;
use App\Models\CustomerMail;
use App\Models\SfGuardUser;
use App\Models\Status;
use App\Models\Sym\TravelCountry;
use App\Models\TravelAgenda;
use App\Models\TravelCompany;
use App\Models\Sym\TravelCountry;
use App\Models\TravelCountryService;
use App\Repositories\CustomerMailRepository;
use Carbon\Carbon;
use DataTables;
use Request;
class RequestController extends Controller
{
@ -22,15 +22,27 @@ class RequestController extends Controller
public function __construct()
{
$this->middleware(['admin', '2fa']);
$this->middleware(['admin', '2fa']);
}
public function index($step = false)
{
// Get distinct travel_country_ids from bookings that are not null
$usedCountryIds = Booking::whereNotNull('travel_country_id')
->distinct()
->pluck('travel_country_id');
// Fetch the corresponding TravelCountry models and create an associative array [id => name]
$travel_countries = TravelCountry::whereIn('id', $usedCountryIds)
->pluck('name', 'id') // Use country id as key
->toArray();
//dd($travel_countries); // Keep this for debugging if needed
$travel_countries = Booking::join('travel_country', 'travel_country_id', '=', 'travel_country.id')->get()->pluck('name', 'travel_country_id')->unique()->toArray();
$filter_lead_status = Status::get()->pluck('name', 'id')->toArray();
$filter_travel_company = TravelCompany::get()->pluck('name', 'id')->toArray();
$filter_airports = Airport::get()->pluck('name', 'id')->toArray();
$filter_paying_out = Booking::$paying_out_types;
$filter_paying_out_status = Booking::$paying_out_status_types;
$filter_refund = Booking::$refund_types;
@ -56,8 +68,10 @@ class RequestController extends Controller
'filter_refund' => $filter_refund,
'filter_xx_tkt' => $filter_xx_tkt,
'filter_airlines' => $filter_airlines,
'filter_sf_guard_user' => $filter_sf_guard_user
'filter_sf_guard_user' => $filter_sf_guard_user,
'filter_airports' => $filter_airports,
];
return view('request.index', $data);
}
@ -75,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;
@ -84,57 +98,62 @@ class RequestController extends Controller
}
die();
*/
private function getSearchRequests(){
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') != ""){
if (Request::get('full_firstname_search') != "") {
$query->whereHas('customer', function ($q) {
$q->where('firstname', 'LIKE', '%'.Request::get('full_firstname_search').'%');
}); }
if(Request::get('full_lastname_search') != ""){
$query->whereHas('customer', function ($q) {
$q->where('name', 'LIKE', '%'.Request::get('full_lastname_search').'%');
$q->where('firstname', 'LIKE', '%' . Request::get('full_firstname_search') . '%');
});
}
if(Request::get('travel_option_country_id') != ""){
$country_ids = TravelCountry::where('contact_lands', 'LIKE', '%"'.Request::get('travel_option_country_id').'"%')->get()->pluck('id');
if (Request::get('full_lastname_search') != "") {
$query->whereHas('customer', function ($q) {
$q->where('name', 'LIKE', '%' . Request::get('full_lastname_search') . '%');
});
}
if (Request::get('travel_option_country_id') != "") {
$country_ids = TravelCountry::where('contact_lands', 'LIKE', '%"' . Request::get('travel_option_country_id') . '"%')->get()->pluck('id');
$country_ids[] = Request::get('travel_option_country_id');
$query->whereIn('travel_country_id', $country_ids);
}
if(Request::get('travel_option_agenda_id') != ""){
if (Request::get('travel_option_agenda_id') != "") {
$query->where('travelagenda_id', '=', Request::get('travel_option_agenda_id'));
}
if(Request::get('travel_option_company_id') != ""){
if (Request::get('travel_option_company_id') != "") {
$query->where('travel_company_id', '=', Request::get('travel_option_company_id'));
}
if(Request::get('travel_option_lead_status_id') != ""){
if (Request::get('travel_option_lead_status_id') != "") {
$query->whereHas('lead', function ($q) {
$q->whereIn('status_id', Request::get('travel_option_lead_status_id'));
});
}
if(Request::get('travel_option_paying_out') != ""){
if (Request::get('travel_option_paying_out') != "") {
$query->where('paying_out', '=', Request::get('travel_option_paying_out'));
}
if(Request::get('travel_option_paying_out_status') != ""){
if (Request::get('travel_option_paying_out_status') != "") {
$query->where('paying_out_status', '=', Request::get('travel_option_paying_out_status'));
}
if(Request::get('travel_option_refund') != ""){
if (Request::get('travel_option_refund') != "") {
$query->where('refund', '=', Request::get('travel_option_refund'));
}
if(Request::get('travel_option_xx_tkt') != ""){
if (Request::get('travel_option_xx_tkt') != "") {
$query->where('xx_tkt', '=', Request::get('travel_option_xx_tkt'));
}
if(Request::get('travel_option_airline_id') != ""){
$query->where('airline_ids', 'LIKE', '%'.Request::get('travel_option_airline_id').'%');
if (Request::get('travel_option_airline_id') != "") {
$query->where('airline_ids', 'LIKE', '%' . Request::get('travel_option_airline_id') . '%');
}
if (Request::get('travel_option_airport_id') != "") {
$query->where('airport_id', '=', Request::get('travel_option_airport_id'));
}
// $query->where('end_date', '<=', $now);
if(Request::get('travel_option_search')){
if (Request::get('travel_option_search')) {
$now = Carbon::now();
switch (Request::get('travel_option_search')){
switch (Request::get('travel_option_search')) {
case 'before_2':
$query->whereBetween('start_date', [Carbon::now()->modify('-2 month'), $now]);
@ -156,242 +175,245 @@ class RequestController extends Controller
break;
}
}else{
} else {
$start = null;
$end = null;
if(Request::get('arrival_start_date') != ""){
if (Request::get('arrival_start_date') != "") {
$arrStart = explode(".", Request::get('arrival_start_date'));
if(count($arrStart) == 3){
if (count($arrStart) == 3) {
$start = Carbon::create($arrStart[2], $arrStart[1], $arrStart[0], 0, 0, 0);
}
}
if(Request::get('arrival_end_date') != ""){
if (Request::get('arrival_end_date') != "") {
$arrEnd = explode(".", Request::get('arrival_end_date'));
if(count($arrEnd) == 3){
if (count($arrEnd) == 3) {
$end = Carbon::create($arrEnd[2], $arrEnd[1], $arrEnd[0], 23, 59, 59);
}
}
if($start && $end){
if ($start && $end) {
$query->whereBetween('start_date', [$start, $end]);
}
if($start && !$end){
if ($start && !$end) {
$query->where('start_date', '>=', $start);
}
if(!$start && $end){
if (!$start && $end) {
$query->where('start_date', '<=', $end);
}
$start = null;
$end = null;
if(Request::get('departure_start_date') != ""){
if (Request::get('departure_start_date') != "") {
$arrStart = explode(".", Request::get('departure_start_date'));
if(count($arrStart) == 3){
if (count($arrStart) == 3) {
$start = Carbon::create($arrStart[2], $arrStart[1], $arrStart[0], 0, 0, 0);
}
}
if(Request::get('departure_end_date') != ""){
if (Request::get('departure_end_date') != "") {
$arrEnd = explode(".", Request::get('departure_end_date'));
if(count($arrEnd) == 3){
if (count($arrEnd) == 3) {
$end = Carbon::create($arrEnd[2], $arrEnd[1], $arrEnd[0], 23, 59, 59);
}
}
if($start && $end){
if ($start && $end) {
$query->whereBetween('end_date', [$start, $end]);
}
if($start && !$end){
if ($start && !$end) {
$query->where('end_date', '>=', $start);
}
if(!$start && $end){
if (!$start && $end) {
$query->where('end_date', '<=', $end);
}
}
if(Request::get('sort_travel_country_id') != ""){
if (Request::get('sort_travel_country_id') != "") {
$query->where('travel_country_id', '=', Request::get('sort_travel_country_id'));
}
if(Request::get('sort_travelagenda_id') != ""){
if (Request::get('sort_travelagenda_id') != "") {
$query->where('travelagenda_id', '=', Request::get('sort_travelagenda_id'));
}
if(Request::get('sort_sf_guard_user_id') != ""){
if (Request::get('sort_sf_guard_user_id') != "") {
$query->where('sf_guard_user_id', '=', Request::get('sort_sf_guard_user_id'));
}
if(Request::get('sort_travel_documents') != ""){
if (Request::get('sort_travel_documents') != "") {
$query->where('travel_documents', '=', Request::get('sort_travel_documents'));
}
if(Request::get('full_lead_id_search') != ""){
$query->where('lead_id', 'LIKE', '%'.Request::get('full_lead_id_search'). '%');
if (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').'%');
if (Request::get('full_booking_id_search') != "") {
$query->where('id', 'LIKE', '%' . Request::get('full_booking_id_search') . '%');
}
return $query;
}
public function getAjaxRequests(){
public function getAjaxRequests()
{
$data = Request::all();
if(Request::ajax()) {
if(isset($data['action']) && $data['action'] === "get_popover_booking_services") {
if (Request::ajax()) {
if (isset($data['action']) && $data['action'] === "get_popover_booking_services") {
$booking = Booking::findOrFail($data['booking_id']);
$ret = "";
$count = false;
foreach($booking->travel_country->getContactLandsModels() as $TravelCountry){
if($TravelCountry->stern_travel_country){
$hl = $TravelCountry->stern_travel_country->name."<br>";
foreach ($booking->travel_country->getContactLandsModels() as $TravelCountry) {
if ($TravelCountry->stern_travel_country) {
$hl = $TravelCountry->stern_travel_country->name . "<br>";
$tmp = "";
foreach($TravelCountry->stern_travel_country->travel_country_services as $travel_country_service){
foreach ($TravelCountry->stern_travel_country->travel_country_services as $travel_country_service) {
$tmp .= \App\Models\BookingCountryService::getStatus($travel_country_service->id, $booking->id) ?
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> '.$travel_country_service->name.'</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> '.$travel_country_service->name.'</span>';
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $travel_country_service->name . '</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> ' . $travel_country_service->name . '</span>';
$tmp .= '<br>';
}
if($tmp !== ""){
$ret .= $hl.$tmp;
if ($tmp !== "") {
$ret .= $hl . $tmp;
}
}
}
if($booking->service_provider_entries->count()){
foreach($booking->service_provider_entries as $service_provider_entry){
if($service_provider_entry->service_provider->service_provider_services->count()){
$hl = $service_provider_entry->service_provider->name."<br>";
if ($booking->service_provider_entries->count()) {
foreach ($booking->service_provider_entries as $service_provider_entry) {
if ($service_provider_entry->service_provider->service_provider_services->count()) {
$hl = $service_provider_entry->service_provider->name . "<br>";
$tmp = "";
foreach($service_provider_entry->service_provider->service_provider_services as $service_provider_service){
foreach ($service_provider_entry->service_provider->service_provider_services as $service_provider_service) {
$tmp .= \App\Models\BookingProviderService::getStatus($service_provider_service->id, $booking->id) ?
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> '.$service_provider_service->name.'</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> '.$service_provider_service->name.'</span>';
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $service_provider_service->name . '</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> ' . $service_provider_service->name . '</span>';
$tmp .= '<br>';
}
if($tmp !== ""){
$ret .= $hl.$tmp;
if ($tmp !== "") {
$ret .= $hl . $tmp;
}
}
}
}
if($booking->booking_service_items->count()){
foreach($booking->booking_service_items as $booking_service_item){
if($booking_service_item->travel_company->travel_company_services->count()){
$hl = $booking_service_item->travel_company->name."<br>";
if ($booking->booking_service_items->count()) {
foreach ($booking->booking_service_items as $booking_service_item) {
if ($booking_service_item->travel_company->travel_company_services->count()) {
$hl = $booking_service_item->travel_company->name . "<br>";
$tmp = "";
foreach($booking_service_item->travel_company->travel_company_services as $travel_company_service){
foreach ($booking_service_item->travel_company->travel_company_services as $travel_company_service) {
$tmp .= \App\Models\BookingCompanyService::getStatus($travel_company_service->id, $booking->id) ?
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> '.$travel_company_service->name.'</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> '.$travel_company_service->name.'</span>';
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $travel_company_service->name . '</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> ' . $travel_company_service->name . '</span>';
$tmp .= '<br>';
}
if($tmp !== ""){
$ret .= $hl.$tmp;
if ($tmp !== "") {
$ret .= $hl . $tmp;
}
}
}
}
return $ret === "" ? 'keine Leistungen definiert' : $ret;
return $ret === "" ? 'keine Leistungen definiert' : $ret;
}
if(isset($data['action']) && $data['action'] === "get_popover_booking_notice"){
if (isset($data['action']) && $data['action'] === "get_popover_booking_notice") {
$booking = Booking::findOrFail($data['booking_id']);
$ret = "";
if($booking->booking_notices->count()){
if ($booking->booking_notices->count()) {
$booking_notice = $booking->booking_notices->first();
return $booking_notice->getSmallerMessage(500);
}
return $ret === "" ? 'keine E-Notiz' : $ret;
return $ret === "" ? 'keine E-Notiz' : $ret;
}
if(isset($data['action']) && $data['action'] === "get_popover_booking_last_email"){
if (isset($data['action']) && $data['action'] === "get_popover_booking_last_email") {
$booking = Booking::findOrFail($data['booking_id']);
$ret = "";
if($booking->customer_mails->count()){
if ($booking->customer_mails->count()) {
$customer_mail = $booking->customer_mails_sent_at->last();
return "<h6>".$customer_mail->subject."</h6>".$customer_mail->message;
}
return $ret === "" ? 'keine E-Mail' : $ret;
return "<h6>" . $customer_mail->subject . "</h6>" . $customer_mail->message;
}
return $ret === "" ? 'keine E-Mail' : $ret;
}
if(isset($data['action']) && $data['action'] === "get_popover_booking_participants_pass"){
if (isset($data['action']) && $data['action'] === "get_popover_booking_participants_pass") {
$booking = Booking::findOrFail($data['booking_id']);
$ret = "";
if($booking->participant_firstname){
$ret .= $booking->participant_pass ?
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> '.$booking->participant_firstname." ".$booking->participant_lastname.'</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> '.$booking->participant_firstname." ".$booking->participant_lastname.'</span>';
$ret .= "<br>";
if ($booking->participant_firstname) {
$ret .= $booking->participant_pass ?
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $booking->participant_firstname . " " . $booking->participant_lastname . '</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> ' . $booking->participant_firstname . " " . $booking->participant_lastname . '</span>';
$ret .= "<br>";
}
if($booking->participants->count()){
foreach($booking->participants as $participant){
if ($booking->participants->count()) {
foreach ($booking->participants as $participant) {
$ret .= $participant->participant_pass ?
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> '.$participant->participant_firstname." ".$participant->participant_lastname.'</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> '.$participant->participant_firstname." ".$participant->participant_lastname.'</span>';
'<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $participant->participant_firstname . " " . $participant->participant_lastname . '</span>' :
'<span class="badge badge-pill badge-danger""><i class="fa fa-times"></i> ' . $participant->participant_firstname . " " . $participant->participant_lastname . '</span>';
$ret .= "<br>";
}
}
return $ret === "" ? 'keine Teilnehmer' : $ret;
}
$query = $this->getSearchRequests();
$ret = $query->get()->pluck('travelagenda_id', 'id')->unique()->toArray();
return TravelAgenda::whereIn('id', $ret)->get()->pluck('name', 'id');
}
}
public function loadModal(){
public function loadModal()
{
$data = Request::all();
$ret = "";
if(Request::ajax()){
if($data['action'] === 'new-customer-mail'){
if (Request::ajax()) {
if ($data['action'] === 'new-customer-mail') {
$data['customers'] = [];
$query = $this->getSearchRequests();
$bookings = $query->orderBy('id', 'DESC')->limit(50)->get();
foreach ($bookings as $booking){
foreach ($bookings as $booking) {
$tmp = [];
$tmp['email'] = $booking->customer ? $booking->customer->email : "";
$tmp['name'] = $booking->customer ? $booking->customer->firstname." ".$booking->customer->name." | " : "- | ";
$tmp['name'] .= $booking->travel_country_id ? $booking->travel_country->name." | " : "- | ";
$tmp['name'] .= $booking->travelagenda_id ? $booking->travel_agenda->name."" : "-";
$tmp['name'] = $booking->customer ? $booking->customer->firstname . " " . $booking->customer->name . " | " : "- | ";
$tmp['name'] .= $booking->travel_country_id ? $booking->travel_country->name . " | " : "- | ";
$tmp['name'] .= $booking->travelagenda_id ? $booking->travel_agenda->name . "" : "-";
$data['customers'][$booking->id] = $tmp;
}
$ret = CustomerMailRepository::loadModal($data);
}
if($data['action'] === 'show-customer-mail'){
if ($data['action'] === 'show-customer-mail') {
$booking = Booking::findOrFail($data['booking_id']);
$ret = "";
if($booking->customer_mails->count()){
if ($booking->customer_mails->count()) {
$ret = CustomerMailRepository::loadModal($data);
}
}
}
}
return response()->json(['response' => $data, 'html'=>$ret]);
return response()->json(['response' => $data, 'html' => $ret]);
}
public function getRequests()
{
$query = $this->getSearchRequests();
return DataTables::eloquent($query)
->addColumn('action_booking_edit', function (Booking $booking) {
return '<a href="' . route('booking_detail', [$booking->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('action_booking_edit', function (Booking $booking) {
return '<a href="' . route('booking_detail', [$booking->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('id', function (Booking $booking) {
return '<a data-order="'.$booking->id.'" href="'.make_old_url('booking/'.$booking->id.'/edit').'" data-id="'.$booking->id.'">'.$booking->id.'</a>';
return '<a data-order="' . $booking->id . '" href="' . make_old_url('booking/' . $booking->id . '/edit') . '" data-id="' . $booking->id . '">' . $booking->id . '</a>';
})
->addColumn('action_lead_edit', function (Booking $booking) {
return '<a href="' . route('lead_detail', [$booking->lead_id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
return '<a href="' . route('lead_detail', [$booking->inquiry_id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('lead_id', function (Booking $booking) {
return '<a data-order="'.$booking->lead_id.'" href="'.make_old_url('leads/'.$booking->lead_id.'/edit').'" data-id="'.$booking->lead_id.'">'.$booking->lead_id.'</a>';
return '<a data-order="' . $booking->inquiry_id . '" href="' . make_old_url('leads/' . $booking->inquiry_id . '/edit') . '" data-id="' . $booking->inquiry_id . '">' . $booking->inquiry_id . '</a>';
})
->addColumn('travel_country_id', function (Booking $booking) {
return '<span data-order="'.($booking->travel_country_id ? $booking->travel_country_id : 0).'">'.($booking->travel_country_id ? $booking->travel_country->name : "-").'</span>';
return '<span data-order="' . ($booking->travel_country_id ? $booking->travel_country_id : 0) . '">' . ($booking->travel_country_id ? $booking->travel_country->name : "-") . '</span>';
})
->addColumn('travelagenda_id', function (Booking $booking) {
return '<span data-order="'.($booking->travelagenda_id ? $booking->travelagenda_id : 0).'">'.($booking->travelagenda_id ? $booking->travel_agenda->name : "-").'</span>';
return '<span data-order="' . ($booking->travelagenda_id ? $booking->travelagenda_id : 0) . '">' . ($booking->travelagenda_id ? $booking->travel_agenda->name : "-") . '</span>';
})
->addColumn('travel_company_id', function (Booking $booking) {
return '<span data-order="'.($booking->travel_company_id ? $booking->travel_company_id : 0).'">'.($booking->travel_company ? $booking->travel_company->name : "-").'</span>';
return '<span data-order="' . ($booking->travel_company_id ? $booking->travel_company_id : 0) . '">' . ($booking->travel_company ? $booking->travel_company->name : "-") . '</span>';
})
->addColumn('airport_id', function (Booking $booking) {
return '<span data-order="' . ($booking->airport_id ? $booking->airport_id : 0) . '">' . ($booking->airport ? $booking->airport->name : "-") . '</span>';
})
->addColumn('comfort', function (Booking $booking) {
return $booking->comfort ? ' <span data-order="1" class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>' : '<span data-order="0" class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>';
@ -406,92 +428,94 @@ class RequestController extends Controller
return $booking->travel_documents ? '<span data-order="1" class="badge badge-pill badge-success" title="Reiseunterlagen vollständig" data-placement="top" rel="tooltip"><i class="fa fa-check"></i></span>' : '<span data-order="0" class="badge badge-pill badge-danger" title="Reiseunterlagen nicht vollständig" data-placement="top" rel="tooltip"><i class="fa fa-times"></i></span>';
})
->addColumn('booking_services', function (Booking $booking) {
return $booking->hasBookingServicesUnchecked() ? '<span data-order="1" class="badge badge-pill badge-success" data-booking_id="'.$booking->id.'" data-action="get_popover_booking_services" data-placement="top" data-toggle="popover" title="ServiceLeistungen"><i class="fa fa-check"></i></span>' :
'<span data-order="0" class="badge badge-pill badge-danger" data-booking_id="'.$booking->id.'" data-action="get_popover_booking_services" data-placement="top" data-toggle="popover" title="ServiceLeistungen"><i class="fa fa-times"></i></span>';
return $booking->hasBookingServicesUnchecked() ? '<span data-order="1" class="badge badge-pill badge-success" data-booking_id="' . $booking->id . '" data-action="get_popover_booking_services" data-placement="top" data-toggle="popover" title="ServiceLeistungen"><i class="fa fa-check"></i></span>' :
'<span data-order="0" class="badge badge-pill badge-danger" data-booking_id="' . $booking->id . '" data-action="get_popover_booking_services" data-placement="top" data-toggle="popover" title="ServiceLeistungen"><i class="fa fa-times"></i></span>';
})
->addColumn('booking_notice', function (Booking $booking) {
return $booking->booking_notices->count() ? '<span data-order="1" class="badge badge-pill badge-success" data-booking_id="'.$booking->id.'" data-action="get_popover_booking_notice" data-placement="top" data-toggle="popover" title="letzte Notiz"><i class="fa fa-check"></i></span>' :
return $booking->booking_notices->count() ? '<span data-order="1" class="badge badge-pill badge-success" data-booking_id="' . $booking->id . '" data-action="get_popover_booking_notice" data-placement="top" data-toggle="popover" title="letzte Notiz"><i class="fa fa-check"></i></span>' :
'<span data-order="0" class="badge badge-pill badge-danger" title="keine Notiz" data-placement="top" rel="tooltip"><i class="fa fa-times"></i></span>';
})
->addColumn('sf_guard_user_id', function (Booking $booking) {
return '<span data-order="'.($booking->sf_guard_user_id ? $booking->sf_guard_user_id : 0).'">'.($booking->sf_guard_user_id? $booking->sf_guard_user->first_name." ".$booking->sf_guard_user->last_name : "-").'</span>';
return '<span data-order="' . ($booking->sf_guard_user_id ? $booking->sf_guard_user_id : 0) . '">' . ($booking->sf_guard_user_id ? $booking->sf_guard_user->first_name . " " . $booking->sf_guard_user->last_name : "-") . '</span>';
})
->addColumn('lead.status_id', function (Booking $booking) {
if($booking->lead){
if ($booking->lead) {
return $booking->lead->getStatusBadge($booking);
}
return '<span data-order="0">-</span>';
})
->addColumn('last_customer_email', function (Booking $booking) {
if($booking->customer_mails->count()){
$customer_mail = $booking->customer_mails_sent_at->last();
$badge = $customer_mail->is_answer ? 'badge-default' : 'badge-secondary';
$badge = !$customer_mail->send ? $badge : 'badge-success';
return '<a data-order="'.$customer_mail->getSentAtRaw().'" href="#" data-toggle="modal"
if ($booking->customer_mails->count()) {
$customer_mail = $booking->customer_mails_sent_at->last();
$badge = $customer_mail->is_answer ? 'badge-default' : 'badge-secondary';
$badge = !$customer_mail->send ? $badge : 'badge-success';
return '<a data-order="' . $customer_mail->getSentAtRaw() . '" href="#" data-toggle="modal"
data-target="#modals-load-content"
data-id="show-mail"
data-url="mail"
data-preview="true"
data-booking_id="'.$booking->id.'"
data-customer_mail_id="'.$customer_mail->id.'"
data-booking_id="' . $booking->id . '"
data-customer_mail_id="' . $customer_mail->id . '"
data-action="show-customer-mail"
data-redirect="back"
data-route="'.route('requests_modal_load').'">
<span class="badge '.$badge.'">'
.($customer_mail->send ? '<i class="fa fa-check-circle"></i>' : '<i class="fa fa-times-circle"></i>').' '
.$customer_mail->sent_at.'</span>
data-route="' . route('requests_modal_load') . '">
<span class="badge ' . $badge . '">'
. ($customer_mail->send ? '<i class="fa fa-check-circle"></i>' : '<i class="fa fa-times-circle"></i>') . ' '
. $customer_mail->sent_at . '</span>
</a>';
}
return '<span data-order="">-</span>';
})
->addColumn('booking_participants_pass', function (Booking $booking) {
return $booking->hasBookingParticipantsPass() ? '<span data-order="1" class="badge badge-pill badge-success" data-booking_id="'.$booking->id.'" data-action="get_popover_booking_participants_pass" data-placement="top" data-toggle="popover" title="Teilnehmer Pass"><i class="fa fa-check"></i></span>' :
'<span data-order="0" class="badge badge-pill badge-danger" data-booking_id="'.$booking->id.'" data-action="get_popover_booking_participants_pass" data-placement="top" data-toggle="popover" title="Teilnehmer Pass"><i class="fa fa-times"></i></span>';
return $booking->hasBookingParticipantsPass() ? '<span data-order="1" class="badge badge-pill badge-success" data-booking_id="' . $booking->id . '" data-action="get_popover_booking_participants_pass" data-placement="top" data-toggle="popover" title="Teilnehmer Pass"><i class="fa fa-check"></i></span>' :
'<span data-order="0" class="badge badge-pill badge-danger" data-booking_id="' . $booking->id . '" data-action="get_popover_booking_participants_pass" data-placement="top" data-toggle="popover" title="Teilnehmer Pass"><i class="fa fa-times"></i></span>';
})
->addColumn('paying_out', function (Booking $booking) {
$icon = "";
$badge = $booking->getPayingOutColor();
if($booking->paying_out_status == 1){ //offen
if ($booking->paying_out_status == 1) { //offen
$icon = '<i class="fa fa-times-circle"></i> ';
}
if($booking->paying_out_status == 2){ //erledigt
if ($booking->paying_out_status == 2) { //erledigt
$badge = 'success';
$icon = '<i class="fa fa-check-circle"></i> ';
}
return '<span data-order="'.$booking->paying_out.'"><span class="badge badge-'.$booking->getPayingOutColor().'">'.$icon.$booking->getPayingOutType().'</span></span>';
return '<span data-order="' . $booking->paying_out . '"><span class="badge badge-' . $booking->getPayingOutColor() . '">' . $icon . $booking->getPayingOutType() . '</span></span>';
})
->addColumn('paying_out_status', function (Booking $booking) {
return '<span data-order="'.$booking->paying_out_status.'"><span class="badge badge-'.$booking->getPayingOutStatusColor().'">'.$booking->getPayingOutStatusType().'</span></span>';
return '<span data-order="' . $booking->paying_out_status . '"><span class="badge badge-' . $booking->getPayingOutStatusColor() . '">' . $booking->getPayingOutStatusType() . '</span></span>';
})
->addColumn('airline_ids', function (Booking $booking) {
return $booking->airline_ids ? '<span data-order="'.$booking->getAirlinesIDs().'">'. $booking->getAirlinesAsNames().'</span>' : '-';
return $booking->airline_ids ? '<span data-order="' . $booking->getAirlinesIDs() . '">' . $booking->getAirlinesAsNames() . '</span>' : '-';
})
->addColumn('refund', function (Booking $booking) {
return '<span data-order="'.$booking->refund_date.'"><span class="badge badge-'.$booking->getRefundColor().'">'.$booking->getRefundTypeList().'</span></span>';
return '<span data-order="' . $booking->refund_date . '"><span class="badge badge-' . $booking->getRefundColor() . '">' . $booking->getRefundTypeList() . '</span></span>';
})
->addColumn('hold', function (Booking $booking) {
return $booking->hold ? ' <span data-order="1" class="badge badge-pill badge-success" title="Hold" data-placement="top" rel="tooltip"><i class="fa fa-check"></i></span>' : '<span data-order="0" class="badge badge-pill badge-danger" title="Hold" data-placement="top" rel="tooltip"><i class="fa fa-times"></i></span>';
})
->addColumn('xx_tkt', function (Booking $booking) {
return '<span data-order="'.$booking->xx_tkt_date.'"><span class="badge badge-'.$booking->getXxTktColor().'">'.$booking->getXxTktTypeList().'</span></span>';
return '<span data-order="' . $booking->xx_tkt_date . '"><span class="badge badge-' . $booking->getXxTktColor() . '">' . $booking->getXxTktTypeList() . '</span></span>';
})
->orderColumn('last_customer_email', function ($query, $order) {
$query->whereHas('customer_mails',
function ($q) use ($order) {
// $q->select('sent_at')->where('sent_at', DB::raw("(select max('sent_at') customer_mails)")); //)
})->orderBy(
$query->whereHas(
'customer_mails',
function ($q) use ($order) {
// $q->select('sent_at')->where('sent_at', DB::raw("(select max('sent_at') customer_mails)")); //)
}
)->orderBy(
CustomerMail::select('sent_at')
->whereColumn('booking_id', 'booking.id')
->orderBy('sent_at', 'DESC')
->limit(1)
, $order);
->limit(1),
$order
);
})
/*
/*
->filterColumn('travelagenda_id', function($query, $keyword) {
if($keyword != ""){
$query->whereRaw("travelagenda_id = ?", $keyword);
@ -499,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')
@ -515,9 +539,7 @@ class RequestController extends Controller
->orderColumn('xx_tkt', 'xx_tkt_date $1')
->orderColumn('comfort', 'comfort $1')
//->orderColumn('travel_documents', 'travel_documents $1')
->rawColumns(['action_lead_edit', 'comfort', 'lead_id', 'booking_participants_pass', 'action_booking_edit', 'travel_country_id', 'travelagenda_id', 'travel_company_id', 'sf_guard_user_id', 'airline_ids', 'lead.status_id', 'last_customer_email', 'id', 'travel_documents', 'booking_services', 'booking_notice', 'paying_out', 'paying_out_status', 'airline_id', 'refund', 'hold', 'xx_tkt'])
->rawColumns(['action_lead_edit', 'comfort', 'lead_id', 'booking_participants_pass', 'action_booking_edit', 'travel_country_id', 'travelagenda_id', 'travel_company_id', 'sf_guard_user_id', 'airline_ids', 'lead.status_id', 'last_customer_email', 'id', 'travel_documents', 'booking_services', 'booking_notice', 'paying_out', 'paying_out_status', 'airline_id', 'airport_id', 'refund', 'hold', 'xx_tkt'])
->make(true);
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Settings;
use Request;
use App\Models\Airport;
use App\Models\Booking;
use App\Http\Controllers\Controller;
class AirportController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function index($step = false)
{
$data = [
'airports' => Airport::all(),
];
return view('settings.airport.index', $data);
}
public function update(){
$data = Request::all();
if($data['id'] === "new"){
$model = Airport::create([
'code' => $data['code'],
'name' => $data['name'],
'city' => $data['city'],
'country' => $data['country'],
'active' => isset($data['active']) ? true : false,
]);
}else{
$model = Airport::find($data['id']);
$model->name = $data['name'];
$model->code = $data['code'];
$model->city = isset($data['city']) ? $data['city'] : NULL;
$model->country = isset($data['country']) ? $data['country'] : NULL;
$model->active = isset($data['active']) ? true : false;
$model->save();
}
\Session()->flash('alert-save', '1');
return redirect(route('admin_settings_airport'));
}
public function delete($id){
if(Booking::where('airport_id', $id)->count()){
\Session()->flash('alert-error', 'Eintrag wird verwendet');
return redirect()->back();
}
$model = Airport::findOrFail($id);
$model->delete();
\Session()->flash('alert-success', 'Eintrag gelöscht');
return redirect()->back();
}
}

View file

@ -18,6 +18,8 @@ class EmailsController extends Controller
protected $identifier_booking_file;
protected $identifier_fewo_file;
protected $identifier_lead_file;
public function __construct()
{

View file

@ -31,6 +31,7 @@ class ServiceProviderController extends Controller
$model = new ServiceProvider();
$id = 'new';
$model->active = 1;
$model->id = 0;
}else{
$model = ServiceProvider::findOrFail($id);
$id = $model->id;
@ -49,7 +50,6 @@ class ServiceProviderController extends Controller
public function update($id){
$data = Request::all();
if(isset($data['update-action'])){
if($data['update-action'] === 'save-service-provider-service'){
$data['active'] = true;//isset($data['active']) ? true : false;
@ -70,7 +70,6 @@ class ServiceProviderController extends Controller
$data['contact_emails'] = isset($data['contact_emails']) ? Util::_explodeLines($data['contact_emails']) : null;
if($id === "new"){
$model = ServiceProvider::create($data);
}else{
@ -87,8 +86,17 @@ class ServiceProviderController extends Controller
public function delete($id, $del="service_provider"){
if($del === 'service_provider') {
abort(404, 'Noch keine Funktion');
//abort(403, 'Noch keine Funktion');
$model = ServiceProvider::findOrFail($id);
if($model->service_provider_entries->count() > 0){
\Session()->flash('alert-error', 'Der Leistungträger kann nicht gelöscht werden, dieser hat Einträge');
return redirect()->back();
}
if($model->service_provider_services->count() > 0){
\Session()->flash('alert-error', 'Der Leistungträger kann nicht gelöscht werden, dieser hat Einträge Service');
return redirect()->back();
}
$model->delete();
\Session()->flash('alert-success', 'Eintrag gelöscht');
return redirect()->back();

View file

@ -48,8 +48,8 @@ class TravelAgendaController extends Controller
public function delete($id){
if(Booking::where('travelagenda_id', $id)->count()){
\Session()->flash('alert-error', 'Eintrag wird verwendet');
if(Booking::where('travelagenda_id', $id)->count()){
\Session()->flash('alert-error', 'Eintrag wird bei Buchnungen verwendet');
return redirect()->back();
}

View file

@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Models\TravelArrivalPoint;
use Request;
class TravelArrivalPointController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function index($step = false)
{
$data = [
'travel_arrival_point' => TravelArrivalPoint::all(),
];
return view('settings.travel_arrival_point.index', $data);
}
public function update(){
$data = Request::all();
if($data['id'] === "new"){
$model = TravelArrivalPoint::create([
'name' => $data['name'],
'active' => isset($data['active']) ? true : false,
'travel_country_id' => $data['travel_country_id'],
]);
}else{
$model = TravelArrivalPoint::find($data['id']);
$model->name = $data['name'];
$model->active = isset($data['active']) ? true : false;
$model->travel_country_id = $data['travel_country_id'];
$model->save();
}
\Session()->flash('alert-save', '1');
return redirect(route('admin_settings_travel_arrival_point'));
}
public function delete($id){
$model = TravelArrivalPoint::findOrFail($id);
if($model->travel_programs->count() > 0){
\Session()->flash('alert-error', 'Eintrag wird bei Reiseprogrammen verwendet');
return redirect()->back();
}
$model->delete();
\Session()->flash('alert-success', 'Eintrag gelöscht');
return redirect()->back();
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Models\TravelCategory;
use Request;
class TravelCategoryController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function index($step = false)
{
$data = [
'travel_category' => TravelCategory::all(),
];
return view('settings.travel_category.index', $data);
}
public function update(){
$data = Request::all();
if($data['id'] === "new"){
$model = TravelCategory::create([
'name' => $data['name'],
'active' => isset($data['active']) ? true : false
]);
}else{
$model = TravelCategory::find($data['id']);
$model->name = $data['name'];
$model->active = isset($data['active']) ? true : false;
$model->save();
}
\Session()->flash('alert-save', '1');
return redirect(route('admin_settings_travel_category'));
}
public function delete($id){
$model = TravelCategory::findOrFail($id);
if($model->bookings->count() > 0){
\Session()->flash('alert-error', 'Eintrag wird bei Buchnungen verwendet');
return redirect()->back();
}
if($model->travel_programs->count() > 0){
\Session()->flash('alert-error', 'Eintrag wird bei Programmen verwendet');
return redirect()->back();
}
if($model->leads->count() > 0){
\Session()->flash('alert-error', 'Eintrag wird bei Anfragen verwendet');
return redirect()->back();
}
$model->delete();
\Session()->flash('alert-success', 'Eintrag gelöscht');
return redirect()->back();
}
}

View file

@ -27,11 +27,11 @@ class TravelCompanyController extends Controller
public function detail($id, $step = false)
{
if($id === "new") {
if ($id == "new") {
$model = new TravelCompany();
$id = 'new';
$model->active = 1;
}else{
} else {
$model = TravelCompany::findOrFail($id);
$id = $model->id;
}
@ -45,20 +45,21 @@ class TravelCompanyController extends Controller
return view('settings.travel_company.detail', $data);
}
public function update($id){
public function update($id)
{
$data = Request::all();
if(isset($data['update-action'])){
if($data['update-action'] === 'save-travel-company-service'){
$data['active'] = true;//isset($data['active']) ? true : false;
if (isset($data['update-action'])) {
if ($data['update-action'] === 'save-travel-company-service') {
$data['active'] = true; //isset($data['active']) ? true : false;
$travel_company = TravelCompany::findOrFail($id);
$data['travel_company_id'] = $travel_company->id;
if($data['travel_company_service_id'] === 'new'){
if ($data['travel_company_service_id'] === 'new') {
$model = TravelCompanyService::create($data);
}else{
} else {
$model = TravelCompanyService::find($data['travel_company_service_id']);
$model->fill($data);
$model->save();
@ -69,9 +70,9 @@ class TravelCompanyController extends Controller
}
$data['contact_emails'] = isset($data['contact_emails']) ? Util::_explodeLines($data['contact_emails']) : null;
if($id === "new"){
if ($id === "new") {
$model = TravelCompany::create($data);
}else{
} else {
$model = TravelCompany::find($id);
$model->fill($data);
$model->save();
@ -79,12 +80,12 @@ class TravelCompanyController extends Controller
\Session()->flash('alert-save', '1');
return redirect(route('admin_settings_travel_company_detail', [$model->id, $data['action']]));
}
public function delete($id, $del="travel_company"){
public function delete($id, $del = "travel_company")
{
if($del === 'travel_country') {
if ($del === 'travel_country') {
abort(404, 'Noch keine Funktion');
$model = TravelCompany::findOrFail($id);
$model->delete();
@ -92,11 +93,11 @@ class TravelCompanyController extends Controller
return redirect()->back();
}
if($del === 'company_service'){
if ($del === 'company_service') {
$service = TravelCompanyService::findOrFail($id);
$travel_company = $service->travel_company;
//check as entry
if($service->booking_company_services->count() > 0){
if ($service->booking_company_services->count() > 0) {
\Session()->flash('alert-error', 'Die Leistung kann nicht gelöscht werden, diese hat Einträge bei den Buchungen');
return redirect(route('admin_settings_travel_company_detail', [$travel_company->id, 'services']));
}
@ -105,7 +106,4 @@ class TravelCompanyController extends Controller
return redirect(route('admin_settings_travel_company_detail', [$travel_company->id, 'services']));
}
}
}

View file

@ -38,11 +38,11 @@ class TravelCountryController extends Controller
public function detail($id, $step = false)
{
if($id === "new") {
if ($id == "new") {
$model = new TravelCountry();
$id = 'new';
$model->active_backend = 1;
}else{
} else {
$model = TravelCountry::findOrFail($id);
$id = $model->id;
}
@ -62,15 +62,15 @@ class TravelCountryController extends Controller
{
$data = Request::all();
if(isset($data['update-action'])){
if($data['update-action'] === 'save-travel-county-service'){
$data['active'] = true;//isset($data['active']) ? true : false;
if (isset($data['update-action'])) {
if ($data['update-action'] === 'save-travel-county-service') {
$data['active'] = true; //isset($data['active']) ? true : false;
$travel_country = TravelCountry::findOrFail($id);
$data['travel_country_id'] = $travel_country->id;
$data['crm_travel_country_id'] = $travel_country->crm_id;
if($data['travel_county_service_id'] === 'new'){
if ($data['travel_county_service_id'] === 'new') {
$model = TravelCountryService::create($data);
}else{
} else {
$model = TravelCountryService::find($data['travel_county_service_id']);
$model->fill($data);
$model->save();
@ -81,7 +81,7 @@ class TravelCountryController extends Controller
}
$data['contact_emails'] = isset($data['contact_emails']) ? Util::_explodeLines($data['contact_emails']) : null;
if(!isset($data['contact_lands'])){
if (!isset($data['contact_lands'])) {
$data['contact_lands'] = null;
}
$data['action'] = isset($data['action']) ? $data['action'] : false;
@ -90,9 +90,9 @@ class TravelCountryController extends Controller
$data['active_frontend'] = isset($data['active_frontend']) ? true : false;
$data['active_backend'] = isset($data['active_backend']) ? true : false;
*/
if($id === "new"){
if ($id === "new") {
$model = TravelCountry::create($data);
}else{
} else {
$model = TravelCountry::find($id);
$model->fill($data);
$model->save();
@ -106,11 +106,11 @@ class TravelCountryController extends Controller
}
//TODO for this time
$tc = \App\Models\Sym\TravelCountry::find($model->crm_id);
if(!$tc){
if (!$tc) {
$tc = \App\Models\Sym\TravelCountry::create($data);
$model->crm_id = $tc->id;
$model->save();
}else {
} else {
$tc->fill($data);
$tc->save();
}
@ -119,17 +119,18 @@ class TravelCountryController extends Controller
}
public function delete($id, $del="travel_country"){
public function delete($id, $del = "travel_country")
{
if($del === 'travel_country'){
if ($del === 'travel_country') {
$model = TravelCountry::findOrFail($id);
if($model->travel_nationality_requirements){
foreach($model->travel_nationality_requirements as $travel_nationality_requirement){
if ($model->travel_nationality_requirements) {
foreach ($model->travel_nationality_requirements as $travel_nationality_requirement) {
$travel_nationality_requirement->delete();
}
}
$tc = \App\Models\Sym\TravelCountry::find($model->crm_id);
if($tc){
if ($tc) {
$tc->delete();
}
$model->delete();
@ -137,7 +138,7 @@ class TravelCountryController extends Controller
return redirect()->back();
}
if($del === 'general_file'){
if ($del === 'general_file') {
$general_file = GeneralFile::findOrFail($id);
$travel_country = $general_file->travel_country;
$fileRepo = new GeneralFileRepository($general_file);
@ -146,23 +147,18 @@ class TravelCountryController extends Controller
$general_file->delete();
\Session()->flash('alert-success', 'Datei gelöscht');
return redirect(route('admin_settings_travel_country_detail', [$travel_country->id, 'data']));
}
if($del === 'country_service'){
if ($del === 'country_service') {
$travel_country_service = TravelCountryService::findOrFail($id);
$travel_country = $travel_country_service->travel_country;
//check as entry
if($travel_country_service->booking_country_services->count() > 0){
if ($travel_country_service->booking_country_services->count() > 0) {
\Session()->flash('alert-error', 'Die Leistung kann nicht gelöscht werden, diese hat Einträge bei den Buchungen');
return redirect(route('admin_settings_travel_country_detail', [$travel_country->id, 'services']));
}
$travel_country_service->delete();
\Session()->flash('alert-success', 'Eintrag gelöscht');
return redirect(route('admin_settings_travel_country_detail', [$travel_country->id, 'services']));
}
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Models\TravelGerneralNote;
use Request;
class TravelGerneralNotesController extends Controller
{
public function __construct()
{
$this->middleware(['superadmin', '2fa']);
}
public function index($step = false)
{
$data = [
'gerneral_notes' => TravelGerneralNote::all(),
];
return view('settings.gerneral_notes.index', $data);
}
public function update(){
$data = Request::all();
if($data['id'] === "new"){
$model = TravelGerneralNote::create([
'name' => $data['name'],
'text' => $data['text']
]);
}else{
$model = TravelGerneralNote::find($data['id']);
$model->name = $data['name'];
$model->text = $data['text'];
$model->save();
}
\Session()->flash('alert-save', '1');
return redirect(route('admin_settings_gerneral_notes'));
}
public function delete($id){
$model = TravelGerneralNote::findOrFail($id);
if($model->travel_programs->count() > 0){
\Session()->flash('alert-error', 'Eintrag wird bei Reiseprogrammen verwendet');
return redirect()->back();
}
$model->delete();
\Session()->flash('alert-success', 'Eintrag gelöscht');
return redirect()->back();
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\SyS;
use App\Http\Controllers\Controller;
use App\Services\SyS\Import;
use App\Services\SyS\ReCalcu;
use App\Services\SyS\ReImport;
class SysController extends Controller
{
public function __construct()
{
$this->middleware(['sysadmin', '2fa']);
}
public function index()
{
return view('sys.index');
}
public function tool($serve)
{
switch ($serve) {
case 'reimport':
return ReImport::show();
break;
case 'calcu':
dd('check App\Services\SyS\ReCalcu');
break;
case 'tree':
dd('check App\Services\SyS\Import');
break;
case 'clean_tree_code':
dd('check App\Services\SyS\Import');
break;
case 'media_insert':
dd('check App\Services\SyS\Import');
break;
case 'import':
dd('check App\Services\SyS\Import');
break;
}
abort(403, 'not found tool');
}
public function store($serve)
{
switch ($serve) {
case 'reimport':
return ReImport::store();
break;
}
abort(403, 'not found tool');
}
}

View file

@ -1,731 +0,0 @@
<?php
namespace App\Http\Controllers\SyS\Tools;
use App\Http\Controllers\Controller;
use App\Models\Booking;
use App\Models\IQContentSite;
use App\Models\IQContentSiteField;
use App\Models\IQContentTree;
use App\Models\IQContentTreeNode;
use App\Models\TravelCountry;
use App\Models\TravelGuide;
use App\Models\TravelNationality;
use App\Models\TravelBooking;
use Illuminate\Support\Str;
use IqContent\LaravelFilemanager\Controllers\FileController;
use IqContent\LaravelFilemanager\Controllers\FolderController;
use IqContent\LaravelFilemanager\Models\IQContentFile;
use Request;
use Validator;
class ContentLinkController extends Controller
{
private $tree = [];
/**
* ContentSiteController constructor.
*/
public function __construct()
{
$this->middleware(['sysadmin', '2fa']);
}
public function filterHTML(){
}
//content_links
public function index()
{
/*$query = Booking::with('booking_draft_items')->select('booking.*')->where('new_drafts', true);
$query->whereHas('booking_draft_items', function ($q) {
$q->where('comfort', true);
});
$bookings = $query->get();
foreach ($bookings as $booking){
$booking->comfort = true;
$booking->save();
}
*/
dd("nothing");
$val = [];
$text = "";
/* $travelGuides = TravelGuide::all();
foreach ($travelGuides as $travelGuide){
if(strpos($travelGuide->full_text, "<h1><br></h1>") !== false){
$val[$travelGuide->id] = "<h1><br></h1>";
}
}
foreach ($travelGuides as $travelGuide){
if(strpos($travelGuide->full_text, "<h1") !== false){
$val[$travelGuide->id] = "<h1";
}
}
$travelGuide = TravelGuide::find(203);
$text = $travelGuide->full_text;
// $new_text = preg_replace('/<h1[^>]*>([\s\S]*?)<\/h1[^>]*>/', '', $TravelGuide->full_text);
*/
$data = [
'text' => $text,
'values' => $val,
];
return view('sys.tools.links', $data);
}
public function store()
{
return redirect()->back();
}
//tree
public function tree()
{
$text = "";
$val = [];
$trees = IQContentTree::all();
foreach ($trees as $tree){
foreach ($tree->iq_content_tree_nodes as $tree_node){
$text .= $tree_node->id." -- ".$tree_node->title."\n";
foreach ($tree_node->iq_content_sites as $site){
if($site->travel_guide && $site->identifier === null){
$identifier = $site->travel_guide->scope === 1 ? 'long' : 'short';
//$site->identifier = $site->travel_guide->scope === 1 ? 'long' : 'short';
$text .= "-- ".$site->id." -- ".$identifier."\n";
}
}
}
}
$data = [
'text' => $text,
'values' => $val,
];
return view('sys.tools.trees', $data);
}
public function treeStore()
{
$trees = IQContentTree::all();
foreach ($trees as $tree){
foreach ($tree->iq_content_tree_nodes as $tree_node){
foreach ($tree_node->iq_content_sites as $site){
if($site->travel_guide){
$site->identifier = $site->travel_guide->scope === 1 ? 'long' : 'short';
$site->save();
}
}
}
}
return redirect()->back();
}
//clean_tree_code
public function cleanTreeCode(){
$val = [];
$text = "";
$travelGuide = TravelGuide::find(166);
$text = $travelGuide->full_text;
$data = [
'new_text' => \App\Services\Util::cleanHTML($text),
'full_text' => $travelGuide->full_text,
'values' => $val,
];
return view('sys.tools.clean', $data);
}
public function cleanTreeCodeStore(){
$this->cleanTextTravelGuide();
return redirect()->back();
}
public function cleanTextTravelGuide()
{
$TravelGuides = TravelGuide::all();
foreach ($TravelGuides as $travelGuide){
$new_text = \App\Services\Util::cleanHTML($travelGuide->full_text);
if(strcmp($travelGuide->full_text, $new_text) != 0){
$travelGuide->full_text = $new_text;
$travelGuide->save();
var_dump($travelGuide->id);
echo "<br>";
}
}
die("done");
}
//media_insert
public function mediaInsert(){
$val = [];
$text = "";
$data = [
'text' => $text,
'values' => $val,
];
return view('sys.tools.insert', $data);
}
public function mediaInsertStore(){
$val = [];
$data = Request::all();
$text = $data['text'];
if($data['action'] === 'insert'){
$lines = explode(PHP_EOL, $text);
$FolderC = new FolderController();
$FileC = new FileController();
foreach ($lines as $line){
$sep = explode(';', $line);
if(isset($sep[0]) && isset($sep[1]) && isset($sep[2])){
//youtube //main dir
$working_dir = "/shares/youtube/".$sep[0];
$folder_name = $sep[1];
$folder = $FolderC->makeFolder($working_dir, $folder_name);
$working_dir = $working_dir."/".$folder_name;
$file = $FileC->makeYoutube($working_dir, $sep[2]);
$val[] = $file.' - '.$sep[2]." | ".$working_dir;
}
}
}
if($data['action'] === 'youtube_ids'){
$TravelGuides = TravelGuide::all();
foreach ($TravelGuides as $travelGuide){
$this->findYoutubeLinks($val, $travelGuide);
/* <iframe allowfullscreen="" frameborder="0" height="500" src="https://www.youtube-nocookie.com/embed/9zs9RKOFCIs?rel=0" width="100%"></iframe> */
}
}
if($data['action'] === 'replace_youtube_links') {
$TravelGuides = TravelGuide::all();
foreach ($TravelGuides as $travelGuide){
$this->replaceYoutubeLinks($val, $travelGuide);
/* <iframe allowfullscreen="" frameborder="0" height="500" src="https://www.youtube-nocookie.com/embed/9zs9RKOFCIs?rel=0" width="100%"></iframe> */
}
}
if($data['action'] === 'replace_youtube_div') {
$this->replaceYoutubeDiv($val, TravelGuide::find(335));
$TravelGuides = TravelGuide::all();
foreach ($TravelGuides as $travelGuide){
$this->replaceYoutubeDiv($val, $travelGuide);
/* <iframe allowfullscreen="" frameborder="0" height="500" src="https://www.youtube-nocookie.com/embed/9zs9RKOFCIs?rel=0" width="100%"></iframe> */
}
}
$data = [
'text' => $text,
'values' => $val,
];
return view('sys.tools.insert', $data);
}
//import
public function import()
{
$text = "";
$val = [];
$data = [
'text' => $text,
'values' => $val,
];
return view('sys.tools.import', $data);
}
public function importStore()
{
$data = Request::all();
$lines = explode(PHP_EOL, $data['text']);
if($data['action'] === 'import_TN'){
foreach ($lines as $line){
$ex = explode(';', $line);
$t_n = TravelNationality::whereName(trim($ex[1]))->first();
if($t_n){
$t_n->nat = $ex[0];
$t_n->save();
}else{
TravelNationality::create([
'name' => trim($ex[1]),
'nat' => $ex[0],
'active' => false,
]
);
}
}
}
if($data['action'] === 'import_CT') {
foreach ($lines as $line){
$ex = explode(';', $line);
$t_c = TravelCountry::whereName(trim($ex[1]))->first();
if($t_c){
dump($t_c->name);
$t_c->destco = $ex[0];
$t_c->save();
$tc = \App\Models\Sym\TravelCountry::find($t_c->crm_id);
$tc->destco = $ex[0];
$tc->save();
}
}
dd("");
}
return redirect()->back();
}
private function replaceYoutubeDiv(&$val, $travelGuide)
{
$ret = [];
$dom = new \DOMDocument('1.0', 'utf-8');
@$dom->loadHTML(mb_convert_encoding($travelGuide->full_text, 'HTML-ENTITIES', 'UTF-8'));
$links = $dom->getElementsByTagName('iframe');
foreach ($links as $link) {
if($link->parentNode->parentNode->nodeName === 'div'){
if($link->parentNode->parentNode->getAttribute('itemprop') === 'video'){
if($link->parentNode->parentNode->getAttribute('class') === 'mediaA'){
if($link->parentNode->parentNode->parentNode->getAttribute('itemprop') === 'video'){
$need = $link->parentNode->parentNode;
$replace = $link->parentNode->parentNode->parentNode;
$replace->parentNode->insertBefore($dom->importNode($need, true), $replace->nextSibling);
$replace->parentNode->removeChild($replace);
$html = $dom->saveHTML();
$travelGuide->full_text = $html;
$travelGuide->save();
$val[] = 'replace - '.$travelGuide->id;
}else{
// dump("notfound itemprop over class mediaA".$travelGuide->id);
$val[] = 'notfound - itemprop over class mediaA - '.$travelGuide->id;
//in
foreach ($link->parentNode->parentNode->childNodes as $node){
if($node->nodeName === 'h2'){
if($node->firstChild->nodeName === '#text'){
$span = $dom->createElement('span', htmlspecialchars($node->nodeValue));
$span->setAttribute('itemprop', 'name');
$h2 = $dom->createElement('h2');
$h2->appendChild($span);
$node->parentNode->replaceChild($h2, $node);
$html = $dom->saveHTML();
$travelGuide->full_text = $html;
$travelGuide->save();
$val[] = 'replace h2 - '.$travelGuide->id;
}
}
}
//<span itemprop="name">
}
//dump("found M div ".$travelGuide->id);
}else{
// dump("notfound class mediaA".$travelGuide->id);
$val[] = 'notfound - class mediaA - '.$travelGuide->id;
}
}else{
// dump("notfound div ".$travelGuide->id);
$val[] = 'notfound - video - '.$travelGuide->id;
}
// dump($travelGuide->id);
}else{
//dump("notfound".$travelGuide->id);
$val[] = 'notfound - div - '.$travelGuide->id;
}
}
return $ret;
}
private function replaceYoutubeLinks(&$val, $travelGuide)
{
$ret = [];
$dom = new \DOMDocument('1.0', 'utf-8');
@$dom->loadHTML(mb_convert_encoding($travelGuide->full_text, 'HTML-ENTITIES', 'UTF-8'));
$links = $dom->getElementsByTagName('iframe');
foreach ($links as $link) {
$src = $link->getAttribute('src');
//
$replace = false;
$reLink = false;
if($link->parentNode->nodeName === 'div'){
if($link->parentNode->getAttribute('class') === 'video-container'){
if($link->parentNode->parentNode->getAttribute('class') === 'mediaA' && $link->parentNode->parentNode->getAttribute('itemprop') !== 'video'){
$replace = $link->parentNode->parentNode;
$reLink = $link->parentNode;
}else{
$val[] = 'found - manual - '.$travelGuide->id;
}
}else{
$val[] = 'first div - manual - '.$travelGuide->id;
}
}elseif ($link->parentNode->nodeName === 'p'){
if($link->parentNode->parentNode->nodeName === 'div'){
if($link->parentNode->parentNode->getAttribute('itemprop') === 'video'){
//replace div
$replace = $link->parentNode->parentNode;
$reLink = $link->parentNode;
}else{
//replace p
$replace = $link->parentNode;
$reLink = $link;
}
}else{
$replace = $link->parentNode;
$reLink = $link;
}
}else{
$val[] = 'else div - manual - '.$travelGuide->id;
}
if($replace){
$id = preg_replace('/http.*?embed\//i', '', $src);
$videoID = preg_replace('#[&\?].+$#', '', $id);
$identifier = Str::slug(pre_slug($videoID), '-');
$IQContentFile = IQContentFile::whereIdentifier($identifier)->first();
if($IQContentFile) {
$video = new \DOMDocument('1.0', 'utf-8');
$makeText = '<?xml encoding="utf-8" ?><div class="mediaA" itemprop="video" itemscope itemtype="http://schema.org/VideoObject">';
$makeText .= '<h2><span itemprop="name">' . $IQContentFile->content['title'] . '</span></h2>';
$makeText .= '<meta itemprop="duration" content="' . $IQContentFile->content['duration'] . '" />';
$makeText .= '<meta itemprop="uploadDate" content="' . $IQContentFile->content['uploadDate'] . '"/>';
$makeText .= '<meta itemprop="thumbnailURL" content="' . $IQContentFile->content['thumbnailURL'] . '" />';
$makeText .= '<meta itemprop="embedURL" content="https://youtube.googleapis.com/v/' . $IQContentFile->content['id'] . '" />';
$makeText .= '<div class="video-container"><iframe src="https://www.youtube.com/embed/' . $IQContentFile->content['id'] . '?rel=0&amp;controls=0&amp;showinfo=0" data-identifier="' . $IQContentFile->identifier . '" data-slug="' . $IQContentFile->slug . '" frameborder="0" allowfullscreen></iframe></div>';
$makeText .= '<div class="mediaInfo"><p class="infotext" itemprop="description">'.$IQContentFile->content['description'].'</p></div></div>';
$makeText = str_replace('&', '&amp;', $makeText);
$video->loadHTML($makeText);
$replace->insertBefore($dom->importNode($video->documentElement, true), $reLink->nextSibling);
$replace->removeChild($reLink);
$html = $dom->saveHTML();
$travelGuide->full_text = $html;
$travelGuide->save();
$val[] = 'replace - '.$travelGuide->id;
}
}
}
return $ret;
}
private function findYoutubeLinks(&$val, $travelGuide)
{
$ret = [];
$dom = new \DOMDocument('1.0', 'utf-8');
@$dom->loadHTML(mb_convert_encoding($travelGuide->full_text, 'HTML-ENTITIES', 'UTF-8'));
$links = $dom->getElementsByTagName('iframe');
foreach ($links as $link) {
$src = $link->getAttribute('src');
$id = preg_replace('/http.*?embed\//i', '', $src);
$videoID = preg_replace('#[&\?].+$#', '', $id);
$identifier = Str::slug(pre_slug($videoID), '-');
$count = IQContentFile::whereIdentifier($identifier)->count();
if($count === 0){
$ret[$videoID] = $count;
$val[] = "weitere;Travel-Guide;".$videoID.";";
}
}
return $ret;
}
//private?
public function h1ToTitleTravelGuide()
{
$TravelGuides = TravelGuide::all();
foreach ($TravelGuides as $travelGuide){
if(strpos($travelGuide->full_text,'<html><body><h1>' )){
$dom = new \DOMDocument('1.0', 'utf-8');
@$dom->loadHTML(mb_convert_encoding($travelGuide->full_text, 'HTML-ENTITIES', 'UTF-8'));
$elements = $dom->getElementsByTagName('h1');
foreach ($elements as $element) {
if($element->nodeValue != ""){
var_dump($travelGuide->id);
var_dump($element->nodeValue);
echo "<br>";
var_dump($travelGuide->name);
echo "<br>";
echo "--";
echo "<br>";
$new_text = preg_replace('/<h1[^>]*>([\s\S]*?)<\/h1[^>]*>/', '', $travelGuide->full_text);
$travelGuide->name = $element->nodeValue;
$travelGuide->full_text = $new_text;
$travelGuide->save();
}
}
}
}
die("done");
}
public function readNodeAndSaveToTree(){
$input = Request::all();
$ret = [];
if(isset($input['text'])){
$out = $this->ul_to_array($input['text']);
$this->array_to_nodes($out);
}
die("done");
$data = [
'text' => $input['text'],
'values' => $ret,
];
return view('iq.tools.links', $data);
}
public function array_to_nodes($array, $lvl = 1, $parent_id = 1, $pos=100){
if(is_array($array)){
foreach ($array as $node){
if(isset($node['slug'])){
$slug = substr($node['slug'], 0, 80);
// $slug = substr($node['slug'], 0, strrpos($slug, " "));
$name = substr($node['name'], 0, 255);
// $name = substr($node['name'], 0, strrpos($name, " "));
$data = [
'tree_id' => 1,
'parent_id' => $parent_id,
'lvl' => $lvl,
'name' => $name,
'identifier' => $slug,
'active' => false,
'pos' => $pos++,
];
$tree_node = IQContentTreeNode::create($data);
$travel_guides = TravelGuide::whereSlug($node['slug'])->get();
foreach ($travel_guides as $travel_guide){
if(IQContentSite::whereTreeNodeId($tree_node->id)->whereTravelGuideId($travel_guide->id)->count() == 0) {
IQContentSite::create(['tree_node_id' => $tree_node->id, 'travel_guide_id' => $travel_guide->id]);
}
}
}
if(isset($node['children']) && is_array($node['children'])){
$this->array_to_nodes($node['children'], $lvl + 1, $tree_node->id, $pos);
}
}
}
}
public function ul_to_array($ol){
if(is_string($ol)){
if(!$ol = simplexml_load_string($ol)) {
trigger_error("Syntax error in UL/LI structure");
return FALSE;
}
return $this->ul_to_array($ol);
} else if(is_object($ol)){
$output = array();
foreach($ol->li as $li){
$tmp = false;
if(isset($li->ol)){
$tmp = $this->ul_to_array($li->ol);
}
if($li->count()){
$str = (string) $li;
$a = new \SimpleXMLElement($li->children()->asXML());
$str = (string) $a[0]." ".$str;
$str = str_replace("\n", "", $str);
$str = str_replace("\r", "", $str);
$str = str_replace("\t", "", $str);
$str = preg_replace(array('/\s{2,}/', '/[\t\n]/'), ' ', $str);
if(trim($str) !== "" && $str !== null){
$slug = (string) $a['href'];
$link = explode("/", $slug);
$link1 = array_pop($link);
$link1 = str_replace('.htm', '',$link1);
$output[] = ['slug'=> $link1, 'name'=>$str, 'children'=>$tmp];
}
}else{
$str = (string) $li;
$str = str_replace("\n", "", $str);
$str = str_replace("\r", "", $str);
$str = str_replace("\t", "", $str);
if(trim($str) !== "" && $str !== null){
$output[] = ['slug'=>'', 'name'=>$str, 'children'=>$tmp];
}
}
}
return $output;
} else {
return FALSE;
}
}
public function rindex()
{
$data = [
'text' => "",
'values' => [],
];
return view('iq.content.tools.redirects', $data);
}
public function rstore()
{
$iqContentTree = IQContentTree::find(2);
$this->makeTree($iqContentTree);
$input = Request::all();
$ret = [];
if(isset($input['text'])){
$dom = new \DOMDocument('1.0', 'utf-8');
@$dom->loadHTML(mb_convert_encoding($input['text'], 'HTML-ENTITIES', 'UTF-8'));
$tags = ['ol'];
foreach ($tags as $tag){
$domElements = [];
$elements = $dom->getElementsByTagName($tag)->item(0);
foreach($elements as $node){
foreach($node->childNodes as $child) {
$ret[] = array($child->nodeName => $child->nodeValue);
}
}
}
/* $tags = ['a']; foreach ($tags as $tag){
$domElements = [];
$elements = $dom->getElementsByTagName($tag);
foreach ($elements as $element) {
$domElements[] = $element;
}
foreach ($domElements as $domElement) {
$r = "-----";
$href = $domElement->getAttribute('href');
$link = explode("/", $href);
$link1 = array_pop($link);
$link1 = str_replace('.html', '',$link1);
$link1 = str_replace('-', '',$link1);
if(isset($this->tree[$link1])){
$r = $this->tree[$link1];
}else{
$link1 = array_pop($link);
$link1 = str_replace('.html', '',$link1);
$link1 = str_replace('-', '',$link1);
if(isset($this->tree[$link1])){
$r = $this->tree[$link1];
}
}
$ret[] = "Redirect 301 /".$href." ".$r;
}
}*/
}
$data = [
'text' => $input['text'],
'values' => $ret,
];
return view('iq.content.tools.redirects', $data);
}
public function makeTree(IQContentTree $iq_content_tree, $lvl = 0, $parent_id = false, $url = "")
{
if ($parent_id) {
//where('active', true)
$tree_nodes = IQContentTreeNode::where('tree_id', $iq_content_tree->id)->where('lvl', $lvl)->where('active', true)->where('parent_id', $parent_id)->orderBy('pos', 'ASC')->get();
} else {
$url = "/" . $iq_content_tree->identifier . "/";
$tree_nodes = IQContentTreeNode::where('tree_id', $iq_content_tree->id)->where('lvl', $lvl)->where('active', true)->orderBy('pos', 'ASC')->get();
}
foreach ($tree_nodes as $node) {
$children = IQContentTreeNode::where('tree_id', $iq_content_tree->id)->where('lvl', $lvl + 1)->where('active', true)->where('parent_id', $node->id)->count();
$this->tree[str_replace('-', '', $node->identifier)] = url($url . $node->identifier);
if ($children) {
$this->makeTree($iq_content_tree, $lvl + 1, $node->id, $url . $node->identifier . "/");
}
}
return true;
}
}

View file

@ -0,0 +1,154 @@
<?php
namespace App\Http\Controllers;
use Request;
use Carbon\Carbon;
use App\Models\Page;
class TravelContentController extends Controller
{
public function __construct()
{
$this->middleware(['admin', '2fa']);
}
public function index($step = false)
{
$data = [
'travelProgramOverviews' => Page::whereTemplate('travelProgramOverview')->get(),
'step' => $step
];
return view('travel.content.index', $data);
}
public function detail($id)
{
if($id === "new") {
$id = 'new';
}else{
$page = Page::findOrFail($id);
$id = $page->id;
}
$data = [
'model' => $page,
'id' => $id,
];
return view('travel.content.detail', $data);
}
public function store($id)
{
$data = Request::all();
if($id === "new") {
//$draft = new Draft();
}else{
$page = Page::findOrFail($id);
}
if(Page::whereSlug($data['slug'])->where('id', '!=', $page->id)->count() > 0){
$data['slug'] = "";
}
$page->title = $data['title'];
$page->title_short = $data['title_short'];
$page->pagetitle = $data['pagetitle'];
$page->slug = $data['slug'];
$page->description = $data['description'];
$page->content_new = $data['content_new'];
$page->status = $data['status'];
$page->show_in_navi = $data['show_in_navi'];
$page->save();
\Session()->flash('alert-save', '1');
return redirect(route('travel_content_detail', [$page->id]));
}
public function subDetail($id)
{
if($id === "new") {
$id = 'new';
}else{
$page = Page::findOrFail($id);
$id = $page->id;
}
$data = [
'model' => $page,
'id' => $id,
];
return view('travel.content.sub_detail', $data);
}
public function subStore($id)
{
$data = Request::all();
if($id === "new") {
//$draft = new Draft();
}else{
$page = Page::findOrFail($id);
}
if(Page::whereSlug($data['slug'])->where('id', '!=', $page->id)->count() > 0){
$data['slug'] = "";
}
$page->title = $data['title'];
$page->pagetitle = $data['pagetitle'];
$page->slug = $data['slug'];
$page->description = $data['description'];
$page->status = $data['status'];
$page->show_in_navi = $data['show_in_navi'];
$page->order = $data['order'];
$page->travel_program = $data['travel_program'] > 0 ? $data['travel_program'] : null;
$page->save();
\Session()->flash('alert-save', '1');
return redirect(route('travel_content_sub_detail', [$page->id]));
}
public function load(){
$data = Request::all();
$ret = "";
if(Request::ajax()) {
if($data['action'] === "modal-copy-page") {
if($data['id'] > 0){
$value = Page::findOrFail($data['id']);
$ret = view('travel.content.modal_copy', compact('value'))->render();
}
}
}
return response()->json(['response' => $data, 'html'=>$ret]);
}
public function update(){
$data = Request::all();
if($data['action'] === 'page-copy'){
if($data['page_copy_id'] > 0){
$model = Page::findOrFail($data['page_copy_id']);
$newModel = $model->replicate();
$newModel->date = null;
//slug unique
// $newModel->slug
$newModel->created_at = now();
$newModel->save();
}
\Session()->flash('alert-save', '1');
return redirect(route('travel_content_detail', [$data['id']]));
}
}
}

View file

@ -48,11 +48,29 @@ class TravelProgramController extends Controller
public function store($id)
{
//TODO new must have an extra funtction!
$data = Request::all();
$program = $this->travelProgramRepo->update($data);
\Session()->flash('alert-save', '1');
return redirect(route('travel_program_detail', [$program->id]));
if(!isset($data['action'])){
abort(403, 'keine Action');
}
//save
$program = $this->travelProgramRepo->updateGeneral($id, $data);
$program = $this->travelProgramRepo->updateDetail($id, $data);
$program = $this->travelProgramRepo->updatePage($id, $data);
\Session()->flash('alert-save', '1');
if($data['action'] === 'saveGeneral'){
return redirect(route('travel_program_detail', [$program->id])."#collapseBookingNotice");
}
if($data['action'] === 'saveDetails'){
return redirect(route('travel_program_detail', [$program->id])."#collapseTravelProgramDetails");
}
if($data['action'] === 'savePage'){
return redirect(route('travel_program_detail', [$program->id])."#collapseTravelProgramPage");
}
return redirect(route('travel_program_detail', [$id]));
}
/*
@ -68,6 +86,7 @@ class TravelProgramController extends Controller
\Session()->flash('alert-save', '1');
return redirect(route('travel_program_detail', [$data['program_id']]));
}
public function classDelete($id){
$travel_class = TravelClass::findOrFail($id);
$pId = $travel_class->program_id;

View file

@ -3,21 +3,23 @@
namespace App\Http\Controllers;
use App\Mail\MailSendFeWoInvoice;
use App\Mail\MailSendFeWoService;
use App\Mail\MailSendFeWoInfo;
use Request;
use App\Services\Util;
use App\Models\CMSContent;
use App\Models\FewoLodging;
use App\Mail\MailSendFeWoInfo;
use App\Models\FewoReservation;
use App\Models\TravelBookingFewoChannel;
use App\Models\TravelUserBookingFewo;
use App\Models\TravelUserBookingFewoNotice;
use App\Models\TravelUserBookingFile;
use App\Repositories\BookingFewoFileRepository;
use App\Repositories\TravelUserBookingFewoRepository;
use App\Services\Util;
use App\Models\CustomerFewoFile;
use App\Mail\MailSendFeWoInvoice;
use App\Mail\MailSendFeWoService;
use Illuminate\Support\Facades\Mail;
use Request;
use App\Models\TravelUserBookingFewo;
use App\Models\TravelUserBookingFile;
use App\Models\TravelBookingFewoChannel;
use App\Models\TravelUserBookingFewoNotice;
use App\Repositories\BookingFewoFileRepository;
use App\Repositories\CustomerFewoFileRepository;
use App\Repositories\TravelUserBookingFewoRepository;
class TravelUserBookingFewoController extends Controller
{
@ -82,6 +84,10 @@ class TravelUserBookingFewoController extends Controller
public function store($id)
{
$data = Request::all();
if(!isset($data['action'])){
abort(403, 'keine Action');
}
if($data['action'] === 'save_notice'){
$travel_user_booking_fewo = $this->userBookingFewoRepo->updateNotice($id, $data);
\Session()->flash('alert-save', '1');
@ -93,7 +99,6 @@ class TravelUserBookingFewoController extends Controller
return redirect(route('travel_user_booking_fewo_detail', [$travel_user_booking_fewo->id])."#collapseBookingNotice");
}
if($data['action'] === 'saveAll'){
return $this->userBookingFewoRepo->update($id, $data);
}
@ -147,8 +152,12 @@ class TravelUserBookingFewoController extends Controller
if($data['action'] === 'sendInfosMailtoUser') {
$travel_user_booking_fewo = TravelUserBookingFewo::findOrFail($id);
if($travel_user_booking_fewo->travel_user_id && $travel_user_booking_fewo->travel_user->email){
$mail_files = [];
if(isset($data['info_mail_files'])){
$mail_files = $this->setMailInfoFiles($data['info_mail_files'], $travel_user_booking_fewo);
}
$mail_bbc = config('mail.mail_bbc');
Mail::to($travel_user_booking_fewo->travel_user->email)->bcc($mail_bbc)->send(new MailSendFeWoInfo($travel_user_booking_fewo));
Mail::to($travel_user_booking_fewo->travel_user->email)->bcc($mail_bbc)->send(new MailSendFeWoInfo($travel_user_booking_fewo, $mail_files));
$send_info_mail = $travel_user_booking_fewo->send_info_mail;
$send_info_mail[] = [date('H:i d.m.Y') => $travel_user_booking_fewo->travel_user->email];
$travel_user_booking_fewo->send_info_mail = $send_info_mail;
@ -195,6 +204,51 @@ class TravelUserBookingFewoController extends Controller
}
}
private function setMailInfoFiles($info_mail_files, $travel_user_booking_fewo)
{
$files = [];
$ret = [];
//read files
foreach($info_mail_files as $mail_file){
if($mail_file === 'fewo_instruction_pdf'){
$files[] = [
'target' => route('customer_file_show', ['fewo_instruction_pdf', $travel_user_booking_fewo->fewo_lodging->id, 'stream']),
'name' => \App\Services\BookingFewo::getFeWoInstructionPDFName($travel_user_booking_fewo->fewo_lodging)
];
}else{
if($file = \App\Models\CMSContent::getModelBySlug($mail_file)){
$files[] = [
'target' => $file->getURL(),
'name' => $file->name
];
}
}
}
//store files
foreach($files as $file){
$arrContextOptions=array(
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
),
);
$contents = file_get_contents($file['target'], false, stream_context_create($arrContextOptions));
$mine = Util::getMimeFromHeader($http_response_header);
$extension = Util::getExtensionFromMime($mine);
$fileRepo = new CustomerFewoFileRepository(new CustomerFewoFile());
$fileRepo->_set('disk', 'travel_user');
$fileRepo->_set('dir', '/attachment/'.date('Y/m').'/');
$fileRepo->_set('travel_user_id', $travel_user_booking_fewo->travel_user_id);
$fileRepo->_set('customer_fewo_mail_id', NULL);
$fileRepo->_set('identifier', 'fewo_info_mail_user');
$fileRepo->_set('originalName', $file['name']);
$fileRepo->_set('mine', $mine);
$fileRepo->_set('extension', $extension);
$ret[] = $fileRepo->storeReturnFile($contents);
}
return $ret;
}
public function getAjaxRequests(){
@ -301,6 +355,7 @@ class TravelUserBookingFewoController extends Controller
})
->addColumn('is_calendar', function (TravelUserBookingFewo $travel_user_booking_fewo) {
$back = "<div class='no-break'>";
$back .= get_active_badge($travel_user_booking_fewo->is_calendar_traum_fewo, "Traum Direkt")." ";
$back .= get_active_badge($travel_user_booking_fewo->is_calendar_fewo_direct, "FEWO Direkt")." ";
$back .= get_active_badge($travel_user_booking_fewo->is_calendar_hrs, "HRS")." ";
$back .= get_active_badge($travel_user_booking_fewo->is_calendar_stern_tours, "STERN TOURS");

View file

@ -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<string, class-string|string>
*/
protected $routeMiddleware = [
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'2fa' => \App\Http\Middleware\MiddleGoogle2FA::class,

View file

@ -1,24 +0,0 @@
<?php
namespace App\Http\Middleware;
use Auth;
use Closure;
use App\Services\AuthGoogle2FA;
class AuthGoogle2FA
{
public function handle($request, Closure $next)
{
$AuthGoogle2FA = app(AuthGoogle2FA::class)->init($request);
if(!Auth::user()->isGoogle2Fa()){
return $AuthGoogle2FA->makeActiveOneTimePasswordResponse();
}
if ($AuthGoogle2FA->isAuthenticated()) {
return $next($request);
}
return $AuthGoogle2FA->makeRequestOneTimePasswordResponse();
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace App\Http\Middleware;
use Auth;
use Closure;
use App\Services\AuthGoogle2FA;
class Google2FA
{
public function handle($request, Closure $next)
{
$AuthGoogle2FA = app(AuthGoogle2FA::class)->init($request);
if(!Auth::user()->isGoogle2Fa()){
return $AuthGoogle2FA->makeActiveOneTimePasswordResponse();
}
if ($AuthGoogle2FA->isAuthenticated()) {
return $next($request);
}
return $AuthGoogle2FA->makeRequestOneTimePasswordResponse();
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use PragmaRX\Google2FALaravel\Support\Authenticator;
use Auth;
class Google2FA
{
public function handle($request, Closure $next)
{
$authenticator = app(Authenticator::class)->boot($request);
dd(Auth::user()->isGoogle2Fa());
if(Auth::user()->isGoogle2Fa()){
}
if ($authenticator->isAuthenticated()) {
return $next($request);
}
return $authenticator->makeRequestOneTimePasswordResponse();
}
}

View file

@ -11,7 +11,6 @@ class MiddleGoogle2FA
public function handle($request, Closure $next)
{
$AuthGoogle2FA = app(AuthGoogle2FA::class)->init($request);
if(!Auth::user()->isGoogle2Fa()){
\App\Services\MyGoogle2FA::logout();
return $AuthGoogle2FA->makeActiveOneTimePasswordResponse();

View file

@ -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;
}

View file

@ -1,8 +1,9 @@
<?php
namespace App\Services;
namespace App\Libraries;
use App\Libraries\CouponPDF;
use App\Models\Coupon;
use App\Services\Util;
use App\Libraries\CouponPDF;
class CreateCouponPDF {

View file

@ -0,0 +1,63 @@
<?php
namespace App\Libraries;
use App\Libraries\CouponPDF;
use App\Models\Coupon;
use Storage;
class CreatePDF{
protected $view;
protected $pdf;
protected $prepath;
protected $disk;
public function __construct($view, $disk = 'public')
{
$this->view = $view;
$this->disk = $disk;
$this->prepath = Storage::disk($disk)->path('');
}
public function create($data, $name='test.pdf', $output='stream', $path = false){
header('Content-type: text/html; charset=UTF-8') ;//chrome
//dd($data);
$pdf = app('dompdf.wrapper');
$pdf->getDomPDF()->set_option("enable_php", true);
$pdf->loadView($this->view, $data);
$pdf->setPaper('A4', 'portrait');
if($output === 'stream'){
//file???
return $pdf->stream($name);
}
if($output === 'download'){
//download
return $pdf->download($name);
}
if($output === 'save'){
if($path){
$pdf->save($path.$name);
return $path.$name;
}
}
}
public function merger($dir, $filename, $template){
$pdfMerger = new MyPDFMerger();
$pdfMerger->addPDF($this->prepath.$dir.$filename);
$file = $pdfMerger->myMerge('string', $filename, $template);
Storage::disk($this->disk)->put($dir.$filename, $file);
}
public function setPrePath($path){
$this->prepath = $path;
}
}
?>

View file

@ -1,11 +1,11 @@
<?php
namespace App\Services;
namespace App\Libraries;
use App\Libraries\CouponPDF;
use App\Models\Coupon;
use Storage;
class CreatePDF{
class CreatePDFCoupon{
protected $view;
protected $pdf;
@ -24,7 +24,9 @@ class CreatePDF{
$pdf = app('dompdf.wrapper');
$pdf->getDomPDF()->set_option("enable_php", true);
$pdf->loadView($this->view, $data);
$pdf->setPaper('A4', 'portrait');
$customPaper = array(0, 0, 595.28, 283,47);
$pdf->setPaper($customPaper, 'portrait');
// $pdf->setPaper('A4', 'portrait');
if($output === 'stream'){
return $pdf->stream($name);

View file

@ -0,0 +1,190 @@
<?php
namespace App\Libraries;
//use FPDI in myMerge v2
//use FPDF;
//use FPDI;
class MyPDFMerger
{
private $_files; //['form.pdf'] ["1,2,4, 5-19"]
private $_fpdi;
public function __construct()
{
/* if(!class_exists("FPDF")) {
require_once(__DIR__.'/fpdf/fpdf.php');
}
if(!class_exists("FPDI")) {
require_once(__DIR__.'/fpdi/fpdi.php');
}*/
}
public function addPDF($filepath, $pages = 'all')
{
if (file_exists($filepath)) {
if (strtolower($pages) != 'all') {
$pages = $this->_rewritepages($pages);
}
$this->_files[] = array($filepath, $pages);
} else {
throw new \exception("Could not locate PDF on '$filepath'");
}
return $this;
}
public function myMerge($outputmode = 'browser', $outputpath = 'newfile.pdf', $theme = false)
{
//$theme = false;
if (!isset($this->_files) || !is_array($this->_files)): throw new \exception("No PDFs to merge.");
endif;
$fpdi = new \setasign\Fpdi\Fpdi();
$first = 1;
//merger operations
foreach ($this->_files as $file) {
$filename = $file[0];
$filepages = $file[1];
$count = $fpdi->setSourceFile($filename);
//add the pages
if ($filepages == 'all') {
for ($i = 1; $i <= $count; $i++) {
$count = $fpdi->setSourceFile($filename);
$template = $fpdi->importPage($i);
$size = $fpdi->getTemplateSize($template);
$orientation = ($size['height'] > $size['width']) ? 'P' : 'L';
$fpdi->AddPage($orientation, array($size['width'], $size['height']));
if ($theme) {
$fpdi->setSourceFile(__DIR__ . '/../../public/pdf/' . $theme . '-' . $first . '.pdf');
if ($first == 1) {
$first = 2;
}
$backId = $fpdi->importPage(1);
$fpdi->useTemplate($backId);
}
$fpdi->useTemplate($template);
}
} else {
foreach ($filepages as $page) {
$count = $fpdi->setSourceFile($filename);
if (!$template = $fpdi->importPage($page)): throw new \exception("Could not load page '$page' in PDF '$filename'. Check that the page exists.");
endif;
$size = $fpdi->getTemplateSize($template);
$orientation = ($size['h'] > $size['w']) ? 'P' : 'L';
$fpdi->AddPage($orientation, array($size['w'], $size['h']));
if ($theme) {
$fpdi->setSourceFile(__DIR__ . '/../../public/pdf/' . $theme . '-' . $first . '.pdf');
if ($first == 1) {
$first = 2;
}
$backId = $fpdi->importPage(1);
$fpdi->useTemplate($backId);
}
$fpdi->useTemplate($template);
}
}
//after first file (invoice) on bpaper
$slug = false;
}
//output operations
$mode = $this->_switchmode($outputmode);
if ($mode == 'S') {
return $fpdi->Output($outputpath, 'S');
} else {
if ($fpdi->Output($outputpath, $mode) == '') {
return true;
} else {
throw new \exception("Error outputting PDF to '$outputmode'.");
return false;
}
}
}
/**
* FPDI uses single characters for specifying the output location. Change our more descriptive string into proper format.
* @param $mode
* @return Character
*/
private function _switchmode($mode)
{
switch (strtolower($mode)) {
case 'download':
return 'D';
break;
case 'browser':
return 'I';
break;
case 'file':
return 'F';
break;
case 'string':
return 'S';
break;
default:
return 'I';
break;
}
}
/**
* Takes our provided pages in the form of 1,3,4,16-50 and creates an array of all pages
* @param $pages
* @return array
* @throws exception
*/
private function _rewritepages($pages)
{
$pages = str_replace(' ', '', $pages);
$part = explode(',', $pages);
//parse hyphens
foreach ($part as $i) {
$ind = explode('-', $i);
if (count($ind) == 2) {
$x = $ind[0]; //start page
$y = $ind[1]; //end page
if ($x > $y): throw new \exception("Starting page, '$x' is greater than ending page '$y'.");
return false;
endif;
//add middle pages
while ($x <= $y): $newpages[] = (int)$x;
$x++;
endwhile;
} else {
$newpages[] = (int)$ind[0];
}
}
return $newpages;
}
}
/*
$pdf = new PDFMerger;
$pdf->addPDF('samplepdfs/one.pdf', '1, 3, 4')
->addPDF('samplepdfs/two.pdf', '1-2')
->addPDF('samplepdfs/three.pdf', 'all')
->merge('file', 'samplepdfs/TEST2.pdf');
//REPLACE 'file' WITH 'browser', 'download', 'string', or 'file' for output options
//You do not need to give a file path for browser, string, or download - just the name.
*/

View file

@ -14,12 +14,15 @@ class MailSendFeWoInfo extends Mailable
protected $travel_user_booking_fewo;
public $subject;
public $files;
public function __construct(TravelUserBookingFewo $travel_user_booking_fewo)
public function __construct(TravelUserBookingFewo $travel_user_booking_fewo, $files = [])
{
$this->travel_user_booking_fewo = $travel_user_booking_fewo;
$this->subject = __('STERN TOURS Anreiseinfo FEWO');
$this->files = $files;
}
public function build()
@ -40,18 +43,33 @@ class MailSendFeWoInfo extends Mailable
'mine' => 'application/pdf',
);
return $this->view('emails.info')
$message = $this->view('emails.info')
->with([
'salutation' => $salutation,
'copy1line' => $this->travel_user_booking_fewo->info_mail_text,
'copy2line' => "",
'greetings' => __('Best regards'),
'model' => $this->travel_user_booking_fewo,
])
->attach($file1['path'], [
'as' => $file1['name'],
'mime' => $file1['mine'],
])
;
]);
$message->attach($file1['path'], [
'as' => $file1['name'],
'mime' => $file1['mine'],
]);
foreach ($this->files as $file) {
$message->attach($file->getPath(),[
'as' => $file->original_name,
'mime' => $file->mine,
]); // attach each file
}
return $message;
}
}
}
//path = \Storage::disk('lead')->path($this->dir.$this->filename);
//name =
//'mine' => 'application/pdf',

View file

@ -21,10 +21,10 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @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()
* @mixin \Eloquent
* @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 Model
{
@ -32,7 +32,10 @@ class Account extends Model
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $casts = [
'deleted_at' => 'datetime',
];
public function user()

View file

@ -30,13 +30,13 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @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 Model
{

54
app/Models/Airport.php Normal file
View file

@ -0,0 +1,54 @@
<?php
/**
* Created by Reliese Model.
*/
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
/**
* 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 Model
{
protected $connection = 'mysql';
protected $table = 'airports';
protected $casts = [
'active' => 'bool'
];
protected $fillable = [
'code',
'name',
'city',
'country',
'active'
];
}

View file

@ -32,12 +32,12 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @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 Model
{

View file

@ -40,6 +40,7 @@ use Illuminate\Database\Eloquent\Model;
* @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 Model
@ -54,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',
@ -85,4 +84,52 @@ class Arrangement extends Model
{
return $this->belongsTo(ArrangementTemplate::class, 'template_id');
}
public function arrangement_type()
{
return $this->belongsTo(ArrangementType::class, 'type_id');
}
public function getDataAsMap()
{
$rawData = $this->attributes['data_s'];
$data = json_decode('{'. $rawData .'}', true);
if (is_array($data))
{
return $data;
}
$entries = preg_split("/[\r\n;]+\s*/", $rawData, -1, PREG_SPLIT_NO_EMPTY);
$ret = array();
foreach ($entries as $entry)
{
$map = preg_split('/:\s*/', $entry, -1, PREG_SPLIT_NO_EMPTY);
if (count($map) == 2)
{
$ret[$map[0]] = $map[1];
}
}
return $ret;
}
public function getDataS()
{
$rawData = $this->attributes['data_s'];
$data = json_decode('{'. $rawData .'}', true);
if (is_array($data))
{
$str = '';
$i = 0;
foreach ($data as $k => $v)
{
if (++$i > 0)
{
$str .= '';
}
$str .= $k .' '. $v;
}
return $str;
}
return $rawData;
}
}

File diff suppressed because it is too large Load diff

View file

@ -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',

View file

@ -28,9 +28,9 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @property int|null $status
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\BookingCountryService whereStatus($value)
* @mixin \Eloquent
*/
class BookingCountryService extends Model
{

View file

@ -0,0 +1,157 @@
<?php
/**
* Created by Reliese Model.
*/
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
/**
* 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 Model
{
protected $table = 'booking_documents';
protected $casts = [
'booking_id' => 'int',
'customer_id' => 'int',
'lead_id' => 'int',
'size' => 'int',
'data' => 'object',
'status' => 'int',
'booking_storno_id' => 'int',
'coupon_id' => 'int',
'date' => 'datetime',
];
protected $fillable = [
'booking_id',
'customer_id',
'lead_id',
'coupon_id',
'booking_storno_id', //
'identifier',
'filename',
'dir',
'original_name',
'ext',
'mine',
'size',
'date',
'data',
'status'
];
/* Je nach identifier können verschiebene stati gesetzt werden
coupon 0 = nicht eingelöst 1 = eingelöst
*/
protected $status_type = [
0 => 'offen',
1 => 'erledigt',
];
public function booking()
{
return $this->belongsTo(Booking::class);
}
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function lead()
{
return $this->belongsTo(Lead::class);
}
public function coupon_id()
{
return $this->belongsTo(Coupon::class);
}
public function booking_storno()
{
return $this->belongsTo(BookingStorno::class);
}
public function getURL($do=false){
return route('storage_file', [$this->id, 'booking_document', $do]);
}
public function getPath(){
return \Storage::disk('public')->path($this->dir.$this->filename);
}
public function deleteFile(){
if(\Storage::disk('public')->exists($this->dir.$this->filename)){
\Storage::disk('public')->delete($this->dir.$this->filename);
}
}
public function formatBytes($precision = 2)
{
$size = $this->size;
if ($size > 0) {
$size = (int) $size;
$base = log($size) / log(1024);
$suffixes = array(' bytes', ' KB', ' MB', ' GB', ' TB');
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
} else {
return $size;
}
}
}

View file

@ -55,7 +55,6 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @property int|null $fewo_lodging_id
* @property float|null $price
* @property-read \App\Models\Booking $booking
@ -64,6 +63,7 @@ use Illuminate\Database\Eloquent\Model;
* @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 Model
{
@ -115,7 +115,7 @@ class BookingDraftItem extends Model
return $this->belongsTo('App\Models\DraftType', 'draft_type_id');
}
public function getItemPrice(){
public function getItemPrice($return = false){
$adult = 0;
$children = 0;
@ -138,9 +138,20 @@ class BookingDraftItem extends Model
$price = $this->price;
}
*/
return ['adult'=>$adult, 'children'=>$children];
}
if($return == 'adult'){
return $adult;
}
if($return == 'children'){
return $children;
}
if($return == 'total'){
return $adult + $children;
}
return ['adult'=>$adult, 'children'=>$children];
}
public function getStartDateAttribute(){
return isset($this->attributes['start_date']) ? Carbon::parse($this->attributes['start_date'])->format("d.m.Y") : '';

View file

@ -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',

View file

@ -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',

View file

@ -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',

View file

@ -6,6 +6,7 @@
namespace App\Models;
use App\Services\Util;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
@ -35,9 +36,10 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @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 Model
{
@ -47,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',
@ -69,4 +69,72 @@ class BookingStorno extends Model
{
return $this->belongsTo(Booking::class);
}
public function booking_document()
{
return $this->hasOne(BookingDocument::class, 'booking_storno_id', 'id');
}
public function getTotalFormatted()
{
return Util::_number_format($this->attributes['total']);
}
public function getTotalRaw()
{
return $this->attributes['total'];
}
public function setTotalAttribute($value)
{
$this->attributes['total'] = Util::_clean_float($value);
}
public function getStornoFormatted()
{
return Util::_number_format($this->attributes['storno']);
}
public function getStornoRaw()
{
return $this->attributes['storno'];
}
public function setStornoAttribute($value)
{
$this->attributes['storno'] = Util::_clean_float($value);
}
public function getStornoDateFormatted()
{
return isset($this->attributes['storno_date']) ? Carbon::parse($this->attributes['storno_date'])->format('d.m.Y') : '';
}
public function setStornoDateAttribute($value)
{
if (!$value) {
$this->attributes['storno_date'] = null;
} else {
$this->attributes['storno_date'] = Carbon::parse($value)->format('Y-m-d');
}
}
public function getStornoPrintFormatted()
{
return isset($this->attributes['storno_print']) ? Carbon::parse($this->attributes['storno_print'])->format('d.m.Y') : '';
}
public function setStornoPrintAttribute($value)
{
if (!$value) {
$this->attributes['storno_print'] = null;
} else {
$this->attributes['storno_print'] = Carbon::parse($value)->format('Y-m-d');
}
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* Created by Reliese Model.
*/
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
/**
* 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 Model
{
protected $table = 'booking_voucher_agency';
protected $casts = [
'booking_id' => 'int',
];
protected $fillable = [
'booking_id',
'binary_data'
];
public function booking()
{
return $this->belongsTo(Booking::class);
}
}

View file

@ -24,9 +24,9 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @property string|null $description
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSAuthor whereDescription($value)
* @mixin \Eloquent
*/
class CMSAuthor extends Model
{

View file

@ -4,41 +4,42 @@ namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Model;
/**
* 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 float|null $decimal
* @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|\App\Models\CMSContent findSimilarSlugs($attribute, $config, $slug)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereDecimal($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereField($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereFullText($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereInteger($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereText($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereUpdatedAt($value)
* @mixin \Eloquent
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent query()
* @property string|null $identifier
* @property array|null $object
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereIdentifier($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent whereObject($value)
* @property int|null $pos
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\CMSContent wherePos($value)
* @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 Model
{
@ -74,7 +75,7 @@ class CMSContent extends Model
'pos' => 'int'
];
public function sluggable()
public function sluggable(): array
{
return [
'slug' => [

View file

@ -34,12 +34,12 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @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 Model
{
@ -61,7 +61,8 @@ class CMSInfo extends Model
'name', 'slug', 'type', 'text', 'full_text', 'integer', 'decimal', 'bool'
];
public function sluggable()
public function sluggable(): array
{
return [
'slug' => [

View file

@ -2,7 +2,7 @@
namespace App\Models;
use App\Services\HTMLHelper;
use App\Helper\HTMLHelper;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
@ -26,13 +26,13 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @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 Model
{

164
app/Models/Contact.php Normal file
View file

@ -0,0 +1,164 @@
<?php
namespace App\Models;
use App\Models\Sym\TravelCountry;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Kontakt-Modell saubere Neuimplementierung auf Basis der customer-Tabelle.
*
* Unterschiede zum alten Customer-Modell:
* - Global Scope schließt zusammengeführte Duplikate (merged_into_id IS NOT NULL) aus
* - merged_into_id + merged_at in $fillable
* - mergedInto() / mergedContacts() Beziehungen
*
* Tabellen-Name: 'contacts' (nach Phase 2 RENAME TABLE customer contacts).
*
* @property int $id
* @property int|null $salutation_id
* @property string|null $title
* @property string|null $name
* @property string|null $firstname
* @property Carbon|null $birthdate
* @property string|null $company
* @property string|null $street
* @property string|null $zip
* @property string|null $city
* @property string|null $email
* @property string|null $phone
* @property string|null $phonebusiness
* @property string|null $phonemobile
* @property string|null $fax
* @property int|null $merged_into_id
* @property Carbon|null $merged_at
* @property Carbon $created_at
* @property Carbon $updated_at
* @property-read Contact|null $mergedInto
* @property-read Collection|Contact[] $mergedContacts
* @property-read Collection|Lead[] $leads
* @property-read Collection|Booking[] $bookings
*/
class Contact extends Model
{
use HasFactory, SoftDeletes;
protected $connection = 'mysql';
protected $table = 'contacts';
protected $casts = [
'salutation_id' => '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',
];
}

View file

@ -26,10 +26,10 @@ use PHPUnit\Framework\Constraint\Count;
* @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)
* @mixin \Eloquent
* @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 Model
{

View file

@ -7,8 +7,9 @@
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use App\Services\Util;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;
/**
* Class Coupon
@ -43,9 +44,10 @@ use Illuminate\Database\Eloquent\Model;
* @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)
* @mixin \Eloquent
* @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 Model
{
@ -57,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',
@ -78,6 +78,32 @@ class Coupon extends Model
'text'
];
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::created(function ($model) {
$model->number = sprintf(
'%02u-%05u-%05u-%02u%02u',
date('y'),
$model->customer_id,
$model->id,
date('H'),
date('i')
);
$model->save();
});
}
public function create($product)
{
logger($product->id);
}
public function booking()
{
return $this->belongsTo(Booking::class);
@ -88,6 +114,12 @@ class Coupon extends Model
return $this->belongsTo(Customer::class);
}
public function booking_document()
{
return $this->hasOne(BookingDocument::class, 'coupon_id', 'id');
}
public function bookings()
{
return $this->hasMany(Booking::class);
@ -95,6 +127,67 @@ class Coupon extends Model
public function isLegal(){
//TODO
// return strtotime(date('Y-m-d')) <= strtotime($this->getValidDate());
return false;
}
public function getValueFormatted()
{
return Util::_number_format($this->attributes['value']);
}
public function getValueRaw()
{
return $this->attributes['value'];
}
public function setValueAttribute($value)
{
$this->attributes['value'] = Util::_clean_float($value);
}
public function getIssueDateFormatted()
{
return isset($this->attributes['issue_date']) ? Carbon::parse($this->attributes['issue_date'])->format('d.m.Y') : '';
}
public function setIssueDateAttribute($value)
{
if (!$value) {
$this->attributes['issue_date'] = null;
} else {
$this->attributes['issue_date'] = Carbon::parse($value)->format('Y-m-d');
}
}
public function getValidDateFormatted()
{
return isset($this->attributes['valid_date']) ? Carbon::parse($this->attributes['valid_date'])->format('d.m.Y') : '';
}
public function setValidDateAttribute($value)
{
if (!$value) {
$this->attributes['valid_date'] = null;
} else {
$this->attributes['valid_date'] = Carbon::parse($value)->format('Y-m-d');
}
}
public function getRedeemDateFormatted()
{
return isset($this->attributes['redeem_date']) ? Carbon::parse($this->attributes['redeem_date'])->format('d.m.Y') : '';
}
public function setRedeemDateAttribute($value)
{
if (!$value) {
$this->attributes['redeem_date'] = null;
} else {
$this->attributes['redeem_date'] = Carbon::parse($value)->format('Y-m-d');
}
}
}

Some files were not shown because too many files have changed in this diff Show more