Compare commits
No commits in common. "f58c709945f5454a5445d31d83ab9dad8fa380e7" and "0341c9c189bbb21ebbaf56bd787c2a8830658c71" have entirely different histories.
f58c709945
...
0341c9c189
2350 changed files with 60025 additions and 289471 deletions
|
|
@ -1,2 +0,0 @@
|
|||
WWWUSER=501
|
||||
WWWGROUP=20
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
WWWUSER=501
|
||||
WWWGROUP=20
|
||||
|
|
@ -1,231 +0,0 @@
|
|||
# Laravel Sail DevContainer Setup
|
||||
|
||||
Diese Dokumentation beschreibt die Einrichtung und Verwendung des DevContainers für das Laravel Sail-Projekt.
|
||||
|
||||
## Übersicht
|
||||
|
||||
Der DevContainer ermöglicht es, das Laravel-Projekt in einer vollständig konfigurierten Docker-Umgebung zu entwickeln, ohne dass lokale PHP-, Node.js- oder andere Abhängigkeiten installiert werden müssen.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Docker Desktop installiert und laufend
|
||||
- Cursor.ai oder VS Code mit DevContainer-Erweiterung
|
||||
- Git (für Repository-Zugriff)
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### 1. DevContainer-Konfiguration (`devcontainer.json`)
|
||||
|
||||
Die Hauptkonfiguration befindet sich in `.devcontainer/devcontainer.json`:
|
||||
|
||||
- **Eigenständiges Docker-Image**: Verwendet `sail-8.4/app` direkt ohne Docker Compose
|
||||
- **Build-Konfiguration**: Definiert Build-Argumente für `WWWUSER` und `WWWGROUP`
|
||||
- **Workspace**: `/var/www/html` (Standard-Laravel-Verzeichnis)
|
||||
- **Benutzer**: `sail` (Laravel Sail Standard-Benutzer)
|
||||
- **Features**: Leer (Tools werden über postCreateCommand installiert)
|
||||
- **Extensions**: PHP, Laravel Blade, Tailwind CSS Extensions
|
||||
- **Port-Forwarding**: Automatisch für wichtige Services (5173, 33061, 6380, 8025)
|
||||
|
||||
### 2. Dockerfile-Anpassungen (`docker/8.4/Dockerfile`)
|
||||
|
||||
Das Dockerfile wurde angepasst um:
|
||||
|
||||
- `ARG WWWUSER` hinzugefügt für korrekte Benutzer-ID
|
||||
- Benutzererstellung mit dynamischen IDs (`$WWWUSER` statt feste `1337`)
|
||||
- Vollständige Laravel-Umgebung mit PHP 8.4, Composer, NPM, Git
|
||||
|
||||
## Installation
|
||||
|
||||
### Automatische Installation (Empfohlen)
|
||||
|
||||
1. Öffnen Sie das Projekt in Cursor.ai oder VS Code
|
||||
2. Klicken Sie auf "Reopen in Container" wenn die DevContainer-Benachrichtigung erscheint
|
||||
3. Warten Sie bis der Container gebaut und gestartet ist
|
||||
|
||||
### Manuelle Installation
|
||||
|
||||
Falls die automatische Installation fehlschlägt, können Sie den Container manuell bauen:
|
||||
|
||||
```bash
|
||||
cd /Users/pandora/Sites/mivita.care
|
||||
docker build --build-arg WWWUSER=501 --build-arg WWWGROUP=20 -f docker/8.4/Dockerfile -t sail-8.4/app docker/8.4
|
||||
```
|
||||
|
||||
## Verfügbare Tools
|
||||
|
||||
Nach der Installation sind folgende Tools verfügbar:
|
||||
|
||||
- **PHP 8.4.12**: Mit Xdebug für Debugging
|
||||
- **Composer**: Für Laravel-Abhängigkeiten
|
||||
- **NPM**: Für Frontend-Assets
|
||||
- **Git**: Für Versionskontrolle
|
||||
- **Node.js**: Für JavaScript-Entwicklung
|
||||
|
||||
Überprüfen Sie die Installation:
|
||||
|
||||
```bash
|
||||
which git
|
||||
which npm
|
||||
which composer
|
||||
php --version
|
||||
```
|
||||
|
||||
## Post-Create-Befehle
|
||||
|
||||
Nach dem Erstellen des Containers werden automatisch ausgeführt:
|
||||
|
||||
1. Composer-Abhängigkeiten (`composer install`)
|
||||
|
||||
## Entwicklung
|
||||
|
||||
### Erste Schritte
|
||||
|
||||
1. **Composer-Abhängigkeiten**: Werden automatisch installiert
|
||||
2. **Laravel-Konfiguration**: `.env` Datei muss manuell erstellt werden (siehe Laravel-Dokumentation)
|
||||
3. **Datenbank-Migrationen**: `php artisan migrate`
|
||||
4. **Asset-Kompilierung**: `npm install && npm run dev`
|
||||
|
||||
### Häufige Befehle
|
||||
|
||||
```bash
|
||||
# Laravel-Befehle
|
||||
php artisan migrate
|
||||
php artisan serve
|
||||
php artisan tinker
|
||||
|
||||
# Composer
|
||||
composer install
|
||||
composer update
|
||||
|
||||
# NPM/Node
|
||||
npm install
|
||||
npm run dev
|
||||
npm run build
|
||||
|
||||
# Sail-Befehle (falls verfügbar)
|
||||
./vendor/bin/sail up
|
||||
./vendor/bin/sail artisan migrate
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container startet nicht
|
||||
|
||||
1. Überprüfen Sie ob Docker Desktop läuft
|
||||
2. Stellen Sie sicher, dass keine anderen Container die benötigten Ports blockieren
|
||||
3. Versuchen Sie einen Clean-Build: `docker build --no-cache ...`
|
||||
|
||||
### Berechtigungsprobleme
|
||||
|
||||
Die Container sind so konfiguriert, dass sie mit Ihrer lokalen Benutzer-ID (`501`) laufen, um Berechtigungsprobleme zu vermeiden.
|
||||
|
||||
### NPM Global Install Probleme
|
||||
|
||||
Falls Sie globale NPM-Pakete installieren möchten, verwenden Sie lokale Installation:
|
||||
|
||||
```bash
|
||||
# Lokale Installation (empfohlen)
|
||||
npm install package-name
|
||||
|
||||
# Oder NPM-Prefix ändern
|
||||
mkdir ~/.npm-global
|
||||
npm config set prefix '~/.npm-global'
|
||||
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### Claude CLI Kompatibilitätsprobleme
|
||||
|
||||
**Problem**: Das `@anthropic-ai/claude-code` Paket ist nicht mit modernen Node.js-Versionen kompatibel.
|
||||
|
||||
**Symptome**:
|
||||
|
||||
- Viele veraltete Abhängigkeiten (deprecated packages)
|
||||
- PhantomJS ARM64 Kompatibilitätsprobleme
|
||||
- Engine-Konflikte mit Node.js v22
|
||||
|
||||
**Lösung**:
|
||||
|
||||
- Verwenden Sie die Cursor.ai-Integration direkt (empfohlen)
|
||||
- Oder verwenden Sie alternative CLI-Tools
|
||||
- Das Paket ist für die Laravel-Entwicklung nicht notwendig
|
||||
|
||||
### Port-Konflikte
|
||||
|
||||
Falls Ports bereits belegt sind, können Sie die Port-Mapping in der DevContainer-Konfiguration anpassen.
|
||||
|
||||
## Erweiterte Konfiguration
|
||||
|
||||
### Zusätzliche Extensions hinzufügen
|
||||
|
||||
Bearbeiten Sie `devcontainer.json` und fügen Sie Extensions zum `extensions` Array hinzu:
|
||||
|
||||
```json
|
||||
"extensions": [
|
||||
"bmewburn.vscode-intelephense-client",
|
||||
"onecentlin.laravel-blade",
|
||||
"shufo.vscode-blade-formatter",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"ihrc.vscode-php-cs-fixer"
|
||||
]
|
||||
```
|
||||
|
||||
### Zusätzliche Tools installieren
|
||||
|
||||
Fügen Sie Tools zum `postCreateCommand` hinzu:
|
||||
|
||||
```json
|
||||
"postCreateCommand": "composer install --no-interaction --prefer-dist --optimize-autoloader && npm install -g your-tool"
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
Bei Problemen mit dem DevContainer:
|
||||
|
||||
1. Überprüfen Sie die Docker-Logs: `docker logs <container-name>`
|
||||
2. Stellen Sie sicher, dass alle Umgebungsvariablen korrekt gesetzt sind
|
||||
3. Versuchen Sie einen Neustart des DevContainers
|
||||
|
||||
## Bekannte Probleme und Lösungen
|
||||
|
||||
### DevContainer-CLI Kompatibilität
|
||||
|
||||
**Problem**: DevContainer-CLI erstellt temporäre Docker-Compose-Dateien die mit der Konfiguration kollidieren.
|
||||
|
||||
**Lösung**: Verwendung eines eigenständigen Docker-Images ohne Docker Compose-Abhängigkeit.
|
||||
|
||||
### Features-Installation
|
||||
|
||||
**Problem**: DevContainer Features können zu Build-Fehlern führen.
|
||||
|
||||
**Lösung**: Tools werden über `postCreateCommand` installiert statt über Features.
|
||||
|
||||
### Root-Berechtigungen
|
||||
|
||||
**Problem**: Der `sail`-Benutzer hat keine Root-Rechte für Paket-Installation.
|
||||
|
||||
**Lösung**: Alle notwendigen Tools sind bereits im Docker-Image installiert.
|
||||
|
||||
### Claude CLI Kompatibilität
|
||||
|
||||
**Problem**: Das `@anthropic-ai/claude-code` Paket ist veraltet und nicht mit Node.js v22 kompatibel.
|
||||
|
||||
**Symptome**:
|
||||
|
||||
- Hunderte von deprecated package warnings
|
||||
- PhantomJS ARM64 Kompatibilitätsprobleme
|
||||
- Engine-Konflikte mit modernen Node.js-Versionen
|
||||
|
||||
**Lösung**:
|
||||
|
||||
- **Verwenden Sie Cursor.ai direkt** (empfohlen) - keine CLI nötig
|
||||
- Das Paket ist für Laravel-Entwicklung nicht notwendig
|
||||
- Alle wichtigen Tools (PHP, Composer, NPM, Git) sind bereits verfügbar
|
||||
|
||||
---
|
||||
|
||||
**Letzte Aktualisierung**: September 2025
|
||||
**Laravel Version**: 11.x
|
||||
**PHP Version**: 8.4.12
|
||||
**Docker Version**: 2.x
|
||||
**Status**: ✅ Vollständig funktionsfähig
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
"name": "Mivita Care (Dev Container)",
|
||||
"dockerComposeFile": [
|
||||
"../docker-compose.yml"
|
||||
],
|
||||
"service": "laravel.test",
|
||||
"workspaceFolder": "/var/www/html",
|
||||
"remoteUser": "sail",
|
||||
"features": {},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"bmewburn.vscode-intelephense-client",
|
||||
"onecentlin.laravel-blade",
|
||||
"shufo.vscode-blade-formatter",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"Anthropic.claude-code",
|
||||
"onecentlin.laravel-extension-pack"
|
||||
]
|
||||
}
|
||||
},
|
||||
// WICHTIG: Hier stehen jetzt nur noch die Dienste, die es im Projekt wirklich gibt!
|
||||
"runServices": [
|
||||
"laravel.test",
|
||||
"horizon"
|
||||
],
|
||||
"containerEnv": {
|
||||
"WWWUSER": "501",
|
||||
"WWWGROUP": "20",
|
||||
"LARAVEL_SAIL": "1"
|
||||
},
|
||||
"mounts": [
|
||||
"source=${localWorkspaceFolder},target=/var/www/html,type=bind,consistency=cached"
|
||||
],
|
||||
// WICHTIG: Nur noch der Vite-Port muss weitergeleitet werden, den Rest macht das Mutterschiff.
|
||||
"forwardPorts": [
|
||||
5173
|
||||
],
|
||||
"portsAttributes": {
|
||||
"5173": {
|
||||
"label": "Vite Dev Server",
|
||||
"onAutoForward": "notify"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
services:
|
||||
laravel.test:
|
||||
build:
|
||||
context: '../docker/8.4'
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
# Führen Sie in Ihrem normalen Terminal `id -u` aus und tragen Sie die Zahl hier ein.
|
||||
WWWUSER: '501'
|
||||
# Führen Sie in Ihrem normalen Terminal `id -g` aus und tragen Sie die Zahl hier ein.
|
||||
WWWGROUP: '20'
|
||||
image: 'sail-8.4/app'
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
ports:
|
||||
- '5173:5173'
|
||||
environment:
|
||||
# Laravel Sail Environment Variables
|
||||
WWWUSER: '501'
|
||||
WWWGROUP: '20'
|
||||
LARAVEL_SAIL: 1
|
||||
XDEBUG_MODE: 'develop,debug'
|
||||
XDEBUG_CONFIG: 'client_host=host.docker.internal'
|
||||
IGNITION_LOCAL_SITES_PATH: '/var/www/html'
|
||||
# Database Configuration
|
||||
DB_CONNECTION: mysql
|
||||
DB_HOST: mysql
|
||||
DB_PORT: 3306
|
||||
DB_DATABASE: mivita
|
||||
DB_USERNAME: sail
|
||||
DB_PASSWORD: password
|
||||
# Application Configuration
|
||||
APP_NAME: Mivita
|
||||
APP_ENV: local
|
||||
APP_DEBUG: true
|
||||
APP_URL: http://localhost
|
||||
# Mail Configuration
|
||||
MAIL_MAILER: smtp
|
||||
MAIL_HOST: mailpit
|
||||
MAIL_PORT: 1025
|
||||
MAIL_USERNAME: null
|
||||
MAIL_PASSWORD: null
|
||||
MAIL_ENCRYPTION: null
|
||||
MAIL_FROM_ADDRESS: hello@example.com
|
||||
MAIL_FROM_NAME: Mivita
|
||||
# Redis Configuration
|
||||
REDIS_HOST: redis
|
||||
REDIS_PASSWORD: null
|
||||
REDIS_PORT: 6379
|
||||
# Vite Configuration
|
||||
VITE_PORT: 5173
|
||||
# Forward Ports
|
||||
FORWARD_DB_PORT: 33061
|
||||
FORWARD_REDIS_PORT: 6380
|
||||
FORWARD_MAILPIT_PORT: 1025
|
||||
FORWARD_MAILPIT_DASHBOARD_PORT: 8025
|
||||
# MySQL Extra Options
|
||||
MYSQL_EXTRA_OPTIONS: --default-authentication-plugin=mysql_native_password
|
||||
volumes:
|
||||
- '../:/var/www/html'
|
||||
networks:
|
||||
- sail
|
||||
depends_on:
|
||||
- mysql
|
||||
- redis
|
||||
- mailpit
|
||||
|
||||
mysql:
|
||||
image: 'mysql/mysql-server:8.0'
|
||||
ports:
|
||||
- '33061:3306'
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
MYSQL_ROOT_HOST: '%'
|
||||
MYSQL_DATABASE: mivita
|
||||
MYSQL_USER: sail
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: 1
|
||||
MYSQL_EXTRA_OPTIONS: --default-authentication-plugin=mysql_native_password
|
||||
volumes:
|
||||
- 'sail-mysql:/var/lib/mysql'
|
||||
- '../docker/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
|
||||
networks:
|
||||
- sail
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- mysqladmin
|
||||
- ping
|
||||
- '-ppassword'
|
||||
retries: 3
|
||||
timeout: 5s
|
||||
|
||||
redis:
|
||||
image: 'redis:alpine'
|
||||
ports:
|
||||
- '6380:6379'
|
||||
volumes:
|
||||
- 'sail-redis:/data'
|
||||
networks:
|
||||
- sail
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- redis-cli
|
||||
- ping
|
||||
retries: 3
|
||||
timeout: 5s
|
||||
|
||||
mailpit:
|
||||
image: 'axllent/mailpit:latest'
|
||||
ports:
|
||||
- '1025:1025'
|
||||
- '8025:8025'
|
||||
networks:
|
||||
- sail
|
||||
|
||||
networks:
|
||||
sail:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
sail-mysql:
|
||||
driver: local
|
||||
sail-redis:
|
||||
driver: local
|
||||
140
.env
140
.env
|
|
@ -3,7 +3,6 @@ APP_ENV=local
|
|||
APP_DEBUG=true
|
||||
APP_KEY=base64:HrWQ9AV3Zt2TU0iq1OeUUpTUaXwNUdh8xHmx7RXTif4=
|
||||
APP_URL=https://mivita.test/
|
||||
APP_URL_CRM=https://my.mivita.test
|
||||
APP_DOMAIN=mivita
|
||||
APP_TLD_CARE=.test
|
||||
APP_TLD_SHOP=.lshop
|
||||
|
|
@ -11,78 +10,52 @@ APP_PROTOCOL=https://
|
|||
APP_URL_MAIN=
|
||||
APP_URL_CHECKOUT=checkout.
|
||||
#APP_URL_MAIN=dev.
|
||||
APP_PRE_URL_CRM=my.
|
||||
APP_URL_PORTAL=in.
|
||||
APP_PRE_URL_PORTAL=in.
|
||||
APP_URL_CRM=my.
|
||||
#APP_CHECKOUT_MAIL=no-reply@mivita.care
|
||||
|
||||
APP_CONTACT_MAIL=kevin.adametz@me.com
|
||||
APP_CHECKOUT_MAIL=kevin.adametz@me.com
|
||||
APP_INFO_MAIL=kevin.adametz@me.com
|
||||
APP_PROORITY_MAIL=kevin.adametz@me.com
|
||||
APP_DEFAULT_MAIL=kevin.adametz@me.com
|
||||
APP_CHECKOUT_TEST_MAIL=kevin.adametz@me.com
|
||||
APP_INFO_TEST_MAIL=kevin.adametz@me.com
|
||||
EXCEPTION_MAIL=exception@adametz.media
|
||||
|
||||
SESSION_DOMAIN=.mivita.test
|
||||
#SESSION_DOMAIN=.mivita.care
|
||||
|
||||
BUSINESS_FORCE_EXECUTE=true
|
||||
|
||||
APP_MODE=test
|
||||
|
||||
APP_IPINFO=true
|
||||
|
||||
APP_MAIN_TAX=1.19
|
||||
APP_MAIN_TAX_RATE=19
|
||||
APP_SHIPPING_TAX=19
|
||||
|
||||
APP_PHP_VERSION=8.2
|
||||
APP_MAIN_TAX = 1.19
|
||||
APP_MAIN_TAX_RATE = 19
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=global-mysql
|
||||
DB_HOST=192.168.1.8
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=mivita
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=password
|
||||
MYSQL_EXTRA_OPTIONS=
|
||||
|
||||
#DB_HOST=192.168.1.8
|
||||
#DB_PORT=3306
|
||||
#DB_DATABASE=mivita
|
||||
#DB_USERNAME=kadmin
|
||||
#DB_PASSWORD=password
|
||||
|
||||
DB_USERNAME=kadmin
|
||||
DB_PASSWORD=KT32vQ7ix
|
||||
|
||||
#DB_DATABASE=d02c1ed2
|
||||
#DB_USERNAME=d02c1ed2
|
||||
#DB_PASSWORD=password
|
||||
#DB_PASSWORD=H7mdYuVTV6pNHDVu
|
||||
|
||||
PAYONE_URL=checkout.mivita.care
|
||||
PAYONE_TS=checkout.mivita.care/transaction/status
|
||||
PAYONE_KEY=Zjop5cvP2UeB7Qhy
|
||||
PAYONE_URL = checkout.mivita.care
|
||||
PAYONE_TS = checkout.mivita.care/transaction/status
|
||||
PAYONE_KEY = Zjop5cvP2UeB7Qhy
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
QUEUE_DRIVER=redis
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
QUEUE_CONNECTION=redis
|
||||
REDIS_HOST=redis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=mailpit
|
||||
MAIL_PORT=1025
|
||||
#MAIL_USERNAME=m04a9fbc
|
||||
#MAIL_PASSWORD=3tQ72oCHZgncCTpK
|
||||
#MAIL_ENCRYPTION=null
|
||||
MAIL_HOST=w017f6e4.kasserver.com
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=m04a9fbc
|
||||
MAIL_PASSWORD=3tQ72oCHZgncCTpK
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
|
||||
PUSHER_APP_ID=
|
||||
|
|
@ -93,87 +66,10 @@ PUSHER_APP_CLUSTER=mt1
|
|||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
|
||||
MIVITA_RENEWAL_DAYS=29
|
||||
MIVITA_REMIND_FIRST_DAYS=21
|
||||
MIVITA_REMIND_SEC_DAYS=14
|
||||
MIVITA_ABO_BOOKING_DAYS=7
|
||||
MIVITA_REMIND_LAST_DAYS=2
|
||||
MIVITA_EDIT_DATA_PASS=mivita
|
||||
MIVITA_ADD_NUMBER_ID=946
|
||||
|
||||
# =============================================================================
|
||||
# =============================================================================
|
||||
# DHL VERSANDMODUL KONFIGURATION (acme/laravel-dhl)
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# DHL KONFIGURATION - PRIORITÄT
|
||||
# =============================================================================
|
||||
# Steuert, welche Konfigurationsquelle Vorrang hat:
|
||||
# - 'database' (Standard): Werte aus Admin-Settings überschreiben .env Werte
|
||||
# - 'env': .env Werte überschreiben Admin-Settings
|
||||
# Nützlich für Test-Server, wo du .env Werte verwenden möchtest,
|
||||
# ohne die Datenbank-Settings ändern zu müssen
|
||||
DHL_CONFIG_SOURCE=env
|
||||
|
||||
# DHL API Zugangsdaten (konsolidiert)
|
||||
DHL_SANDBOX_URL=https://api-sandbox.dhl.com
|
||||
DHL_BASE_URL=https://api-eu.dhl.com
|
||||
DHL_SANDBOX=false
|
||||
DHL_TEST_MODE=false
|
||||
|
||||
DHL_API_KEY=AxGBdF8DBdIAmuhqvG0ASBRKFvyV7ypX
|
||||
|
||||
|
||||
# DHL Standard-Einstellungen
|
||||
DHL_PRODUCT=V01PAK
|
||||
DHL_LABEL_FORMAT=PDF
|
||||
DHL_PRINT_FORMAT=910-300-710
|
||||
DHL_RETOURE_PRINT_FORMAT=910-300-700
|
||||
DHL_PROFILE=STANDARD_GRUPPENPROFIL
|
||||
|
||||
# DHL Queue Configuration
|
||||
# Set to true for background processing (requires queue worker)
|
||||
# Set to false for immediate processing
|
||||
DHL_USE_QUEUE=false
|
||||
|
||||
# DHL Account Numbers (für verschiedene Produkte)
|
||||
DHL_USERNAME=riwa-tec
|
||||
DHL_PASSWORD=MivitaCare!!2025
|
||||
DHL_BILLING_NUMBER=63144073550101
|
||||
DHL_ACCOUNT_NUMBER_DEFAULT=63144073550101
|
||||
DHL_ACCOUNT_NUMBER_V01PAK=63144073550101 # DHL Paket National
|
||||
DHL_ACCOUNT_NUMBER_V62WP=63144073556201 # Warenpost National
|
||||
DHL_ACCOUNT_NUMBER_V53PAK=63144073555301 # DHL Paket International
|
||||
DHL_ACCOUNT_NUMBER_V07PAK=63144073550701 # DHL Retoure Online
|
||||
#sandbox
|
||||
#DHL_USERNAME=user-valid
|
||||
#DHL_PASSWORD=SandboxPasswort2023!
|
||||
#DHL_BILLING_NUMBER=33333333330101
|
||||
#DHL_ACCOUNT_NUMBER_DEFAULT=33333333330101
|
||||
#DHL_ACCOUNT_NUMBER_V01PAK=33333333330102 # DHL Paket National
|
||||
#DHL_ACCOUNT_NUMBER_V62WP=33333333336601 # Warenpost National
|
||||
#DHL_ACCOUNT_NUMBER_V53PAK=33333333335301 # DHL Paket International
|
||||
#DHL_ACCOUNT_NUMBER_V07PAK=33333333330702 # DHL Retoure Online
|
||||
|
||||
#V66WPI|33333333336601
|
||||
#VO1PAK 33333333330102 33333333330101
|
||||
#V53WPAK 33333333335301
|
||||
#VO7PAK 33333333330702
|
||||
|
||||
# DHL Absenderadresse
|
||||
DHL_SENDER_COMPANY="mivita care gmbh"
|
||||
DHL_SENDER_NAME=""
|
||||
DHL_SENDER_STREET="Leinfeld"
|
||||
DHL_SENDER_STREET_NUMBER=2
|
||||
DHL_SENDER_POSTAL_CODE=87755
|
||||
DHL_SENDER_CITY=Kirchhaslach
|
||||
DHL_SENDER_COUNTRY=DE
|
||||
DHL_SENDER_EMAIL=versand@mivita.care
|
||||
DHL_SENDER_PHONE="+49 123 456789"
|
||||
|
||||
# DHL Legacy/Compatibility Settings
|
||||
DHL_API_TYPE=developer
|
||||
DHL_API_SECRET=OyoeePEbYmY1EuOG
|
||||
|
||||
MIVITA_ADD_NUMBER_ID=946
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# =============================================================================
|
||||
# DHL VERSANDMODUL KONFIGURATION - BEISPIEL
|
||||
# =============================================================================
|
||||
# Diese Datei enthält alle DHL-spezifischen Environment-Variablen
|
||||
# Kopieren Sie die benötigten Variablen in Ihre .env Datei
|
||||
|
||||
# DHL API Zugangsdaten
|
||||
# Option 1: DHL Developer API (developer.dhl.com) - Empfohlen
|
||||
DHL_API_KEY=your_api_key_from_developer_dhl_com
|
||||
DHL_API_SECRET=your_api_secret_from_developer_dhl_com
|
||||
|
||||
# Option 2: DHL Business Customer API (Klassisch)
|
||||
DHL_API_USERNAME=your_dhl_username
|
||||
DHL_API_PASSWORD=your_dhl_password
|
||||
DHL_ACCOUNT_NUMBER=your_14_digit_account_number
|
||||
|
||||
# API-Konfiguration
|
||||
DHL_SANDBOX=true # false für Produktion
|
||||
DHL_TEST_MODE=true # false für Produktion
|
||||
DHL_API_TYPE=developer # 'developer' oder 'business_customer'
|
||||
|
||||
# Absenderadresse (Ihre Firmenadresse)
|
||||
DHL_SENDER_COMPANY="Ihr Firmenname"
|
||||
DHL_SENDER_NAME="Versandabteilung"
|
||||
DHL_SENDER_STREET="Ihre Straße"
|
||||
DHL_SENDER_STREET_NUMBER=123
|
||||
DHL_SENDER_POSTAL_CODE=12345
|
||||
DHL_SENDER_CITY="Ihre Stadt"
|
||||
DHL_SENDER_STATE=
|
||||
DHL_SENDER_COUNTRY=DE
|
||||
DHL_SENDER_EMAIL=versand@ihrefirma.de
|
||||
DHL_SENDER_PHONE="+49 123 456789"
|
||||
|
||||
# Standard-Einstellungen
|
||||
DHL_DEFAULT_PRODUCT=V01PAK # V01PAK = DHL Paket
|
||||
DHL_LABEL_FORMAT=PDF # PDF oder ZPL
|
||||
DHL_LABEL_SIZE=A4 # A4 oder 10x15cm
|
||||
DHL_LABEL_STORAGE_PATH=storage/app/dhl/labels
|
||||
DHL_LABEL_PUBLIC_PATH=dhl/labels
|
||||
|
||||
# Retouren
|
||||
DHL_RETURNS_ENABLED=true
|
||||
DHL_RETURN_PRODUCT=V07PAK # V07PAK = DHL Paket Return
|
||||
DHL_RETURN_RECEIVER_ID=your_return_receiver_id
|
||||
|
||||
# Tracking
|
||||
DHL_TRACKING_ENABLED=true
|
||||
DHL_TRACKING_AUTO_UPDATE=true
|
||||
DHL_TRACKING_UPDATE_INTERVAL=60 # Minuten
|
||||
|
||||
# Logging
|
||||
DHL_LOGGING_ENABLED=true
|
||||
DHL_LOGGING_LEVEL=info # debug, info, warning, error
|
||||
DHL_LOG_REQUESTS=true
|
||||
DHL_LOG_RESPONSES=true
|
||||
|
||||
# Queue (verwende bestehende Queue-Konfiguration)
|
||||
DHL_QUEUE_CONNECTION=database
|
||||
DHL_QUEUE_NAME=dhl
|
||||
DHL_QUEUE_TIMEOUT=60
|
||||
DHL_QUEUE_MAX_TRIES=3
|
||||
|
||||
# Fehlerbehandlung
|
||||
DHL_THROW_EXCEPTIONS=true
|
||||
DHL_ERROR_NOTIFICATION_EMAIL=admin@ihrefirma.de
|
||||
|
||||
# Cache
|
||||
DHL_CACHE_ENABLED=true
|
||||
DHL_CACHE_TTL=3600 # Sekunden
|
||||
|
||||
# Entwicklung/Testing
|
||||
DHL_FAKE_API_CALLS=false # true um API-Calls zu simulieren
|
||||
DHL_MOCK_RESPONSES=false # true um Mock-Responses zu verwenden
|
||||
DHL_DEBUG_MODE=false # true für detaillierte Debug-Informationen
|
||||
89
.env.example
89
.env.example
|
|
@ -1,89 +0,0 @@
|
|||
# =============================================================================
|
||||
# DHL VERSANDMODUL KONFIGURATION - BEISPIEL
|
||||
# =============================================================================
|
||||
# Diese Datei enthält alle DHL-spezifischen Environment-Variablen
|
||||
# Kopieren Sie die benötigten Variablen in Ihre .env Datei
|
||||
|
||||
# =============================================================================
|
||||
# DHL KONFIGURATION - PRIORITÄT
|
||||
# =============================================================================
|
||||
# Steuert, welche Konfigurationsquelle Vorrang hat:
|
||||
# - 'database' (Standard): Werte aus Admin-Settings überschreiben .env Werte
|
||||
# - 'env': .env Werte überschreiben Admin-Settings
|
||||
#
|
||||
# Nützlich für Test-Server, wo du .env Werte verwenden möchtest,
|
||||
# ohne die Datenbank-Settings ändern zu müssen
|
||||
DHL_CONFIG_SOURCE=database
|
||||
|
||||
# DHL API Zugangsdaten
|
||||
# Option 1: DHL Developer API (developer.dhl.com) - Empfohlen
|
||||
DHL_API_KEY=your_api_key_from_developer_dhl_com
|
||||
DHL_API_SECRET=your_api_secret_from_developer_dhl_com
|
||||
|
||||
# Option 2: DHL Business Customer API (Klassisch)
|
||||
DHL_API_USERNAME=your_dhl_username
|
||||
DHL_API_PASSWORD=your_dhl_password
|
||||
DHL_ACCOUNT_NUMBER=your_14_digit_account_number
|
||||
|
||||
# API-Konfiguration
|
||||
DHL_SANDBOX=true # false für Produktion
|
||||
DHL_TEST_MODE=true # false für Produktion
|
||||
DHL_API_TYPE=developer # 'developer' oder 'business_customer'
|
||||
|
||||
# Absenderadresse (Ihre Firmenadresse)
|
||||
DHL_SENDER_COMPANY="Ihr Firmenname"
|
||||
DHL_SENDER_NAME="Versandabteilung"
|
||||
DHL_SENDER_STREET="Ihre Straße"
|
||||
DHL_SENDER_STREET_NUMBER=123
|
||||
DHL_SENDER_POSTAL_CODE=12345
|
||||
DHL_SENDER_CITY="Ihre Stadt"
|
||||
DHL_SENDER_STATE=
|
||||
DHL_SENDER_COUNTRY=DE
|
||||
DHL_SENDER_EMAIL=versand@ihrefirma.de
|
||||
DHL_SENDER_PHONE="+49 123 456789"
|
||||
|
||||
# Standard-Einstellungen
|
||||
DHL_DEFAULT_PRODUCT=V01PAK # V01PAK = DHL Paket
|
||||
DHL_LABEL_FORMAT=PDF # PDF oder ZPL
|
||||
DHL_LABEL_SIZE=A4 # A4 oder 10x15cm
|
||||
DHL_LABEL_STORAGE_PATH=storage/app/dhl/labels
|
||||
DHL_LABEL_PUBLIC_PATH=dhl/labels
|
||||
|
||||
# Retouren
|
||||
DHL_RETURNS_ENABLED=true
|
||||
DHL_RETURN_PRODUCT=V07PAK # V07PAK = DHL Paket Return
|
||||
DHL_RETURN_RECEIVER_ID=your_return_receiver_id
|
||||
|
||||
# Tracking
|
||||
DHL_TRACKING_ENABLED=true
|
||||
DHL_TRACKING_AUTO_UPDATE=true
|
||||
DHL_TRACKING_UPDATE_INTERVAL=60 # Minuten
|
||||
|
||||
# Logging
|
||||
DHL_LOGGING_ENABLED=true
|
||||
DHL_LOGGING_LEVEL=info # debug, info, warning, error
|
||||
DHL_LOG_REQUESTS=true
|
||||
DHL_LOG_RESPONSES=true
|
||||
|
||||
# Queue (verwende bestehende Queue-Konfiguration)
|
||||
DHL_QUEUE_CONNECTION=database
|
||||
DHL_QUEUE_NAME=dhl
|
||||
DHL_QUEUE_TIMEOUT=60
|
||||
DHL_QUEUE_MAX_TRIES=3
|
||||
|
||||
# Fehlerbehandlung
|
||||
DHL_THROW_EXCEPTIONS=true
|
||||
DHL_ERROR_NOTIFICATION_EMAIL=admin@ihrefirma.de
|
||||
|
||||
# Cache
|
||||
DHL_CACHE_ENABLED=true
|
||||
DHL_CACHE_TTL=3600 # Sekunden
|
||||
|
||||
# Entwicklung/Testing
|
||||
DHL_FAKE_API_CALLS=false # true um API-Calls zu simulieren
|
||||
DHL_MOCK_RESPONSES=false # true um Mock-Responses zu verwenden
|
||||
DHL_DEBUG_MODE=false # true für detaillierte Debug-Informationen
|
||||
# Business Commands Configuration
|
||||
# Erlaubt Business-Commands an jedem Tag (nur für Test-Server!)
|
||||
# Auf Live-Server muss dies false sein oder weggelassen werden
|
||||
BUSINESS_FORCE_EXECUTE=false
|
||||
59
.gitignore
vendored
59
.gitignore
vendored
|
|
@ -1,42 +1,16 @@
|
|||
# Laravel
|
||||
/.phpunit.cache
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/vendor
|
||||
/storage/*.key
|
||||
/storage/app
|
||||
/storage/framework
|
||||
/storage/language
|
||||
/storage/logs
|
||||
/storage/pail
|
||||
/vendor
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpactor.json
|
||||
.phpunit.result.cache
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
auth.json
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs & Editors
|
||||
/.fleet
|
||||
/.idea
|
||||
/.nova
|
||||
/.vscode
|
||||
/.zed
|
||||
.claude/
|
||||
.cursor/
|
||||
|
||||
# macOS
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
|
|
@ -44,15 +18,18 @@ yarn-error.log
|
|||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
Icon
|
||||
|
||||
|
||||
# Project specific
|
||||
_static/
|
||||
_work/
|
||||
_storage/
|
||||
/vendor
|
||||
/node_modules
|
||||
/storage/language
|
||||
/storage/framework
|
||||
/storage/logs
|
||||
/public/vendor
|
||||
/storage/app
|
||||
|
|
|
|||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Datasource local storage ignored files
|
||||
/dataSources.local.xml
|
||||
/dataSources/
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
3114
.idea/commandlinetools/Laravel_30_07_20__14_22.xml
generated
Normal file
3114
.idea/commandlinetools/Laravel_30_07_20__14_22.xml
generated
Normal file
File diff suppressed because it is too large
Load diff
47
.idea/commandlinetools/schemas/frameworkDescriptionVersion1.1.4.xsd
generated
Normal file
47
.idea/commandlinetools/schemas/frameworkDescriptionVersion1.1.4.xsd
generated
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="framework" type="frameworkType"/>
|
||||
<xs:complexType name="commandType">
|
||||
<xs:all>
|
||||
<xs:element type="xs:string" name="name" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="params" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="help" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="optionsBeforeType" name="optionsBefore" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="frameworkType">
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="extraData" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="commandType" name="command" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="xs:string" name="help" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="invoke" use="required"/>
|
||||
<xs:attribute type="xs:string" name="alias" use="required"/>
|
||||
<xs:attribute type="xs:boolean" name="enabled" use="required"/>
|
||||
<xs:attribute type="xs:integer" name="version" use="required"/>
|
||||
<xs:attribute type="xs:string" name="frameworkId" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="optionsBeforeType">
|
||||
<xs:sequence>
|
||||
<xs:element type="optionType" name="option" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="optionType">
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="help" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="shortcut" use="optional"/>
|
||||
<xs:attribute name="pattern" use="optional">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="space"/>
|
||||
<xs:enumeration value="equals"/>
|
||||
<xs:enumeration value="unknown"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
||||
10
.idea/composerJson.xml
generated
Normal file
10
.idea/composerJson.xml
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ComposerJsonPluginSettings">
|
||||
<unboundedVersionInspectionSettings>
|
||||
<excludedPackages />
|
||||
</unboundedVersionInspectionSettings>
|
||||
<customRepositories />
|
||||
<composerUpdateOptions />
|
||||
</component>
|
||||
</project>
|
||||
11
.idea/dataSources.xml
generated
Normal file
11
.idea/dataSources.xml
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="MySQL - @localhost" uuid="f58b2045-72b3-4199-b31f-5c9c653c31de">
|
||||
<driver-ref>mysql.8</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mysql://localhost:3306/mivita</jdbc-url>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
4
.idea/deployment.xml
generated
Normal file
4
.idea/deployment.xml
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PublishConfigData" serverName="my.mivita.test" />
|
||||
</project>
|
||||
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
85
.idea/jsLinters/jshint.xml
generated
Normal file
85
.idea/jsLinters/jshint.xml
generated
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JSHintConfiguration" version="2.9.5" use-config-file="true" use-custom-config-file="true" custom-config-file-path="$PROJECT_DIR$/assets/plugins/footable/js/.jshintrc">
|
||||
<option asi="false" />
|
||||
<option bitwise="true" />
|
||||
<option boss="false" />
|
||||
<option browser="true" />
|
||||
<option browserify="false" />
|
||||
<option camelcase="false" />
|
||||
<option couch="false" />
|
||||
<option curly="true" />
|
||||
<option debug="false" />
|
||||
<option devel="false" />
|
||||
<option dojo="false" />
|
||||
<option elision="false" />
|
||||
<option enforceall="false" />
|
||||
<option eqeqeq="true" />
|
||||
<option eqnull="false" />
|
||||
<option es3="false" />
|
||||
<option es5="false" />
|
||||
<option esnext="false" />
|
||||
<option evil="false" />
|
||||
<option expr="false" />
|
||||
<option forin="true" />
|
||||
<option freeze="false" />
|
||||
<option funcscope="false" />
|
||||
<option futurehostile="false" />
|
||||
<option gcl="false" />
|
||||
<option globalstrict="false" />
|
||||
<option immed="false" />
|
||||
<option iterator="false" />
|
||||
<option jasmine="false" />
|
||||
<option jquery="false" />
|
||||
<option lastsemic="false" />
|
||||
<option latedef="false" />
|
||||
<option laxbreak="false" />
|
||||
<option laxcomma="false" />
|
||||
<option loopfunc="false" />
|
||||
<option maxerr="50" />
|
||||
<option mocha="false" />
|
||||
<option module="false" />
|
||||
<option mootools="false" />
|
||||
<option moz="false" />
|
||||
<option multistr="false" />
|
||||
<option newcap="false" />
|
||||
<option noarg="true" />
|
||||
<option nocomma="false" />
|
||||
<option node="false" />
|
||||
<option noempty="true" />
|
||||
<option nomen="false" />
|
||||
<option nonbsp="false" />
|
||||
<option nonew="true" />
|
||||
<option nonstandard="false" />
|
||||
<option notypeof="false" />
|
||||
<option noyield="false" />
|
||||
<option onevar="false" />
|
||||
<option passfail="false" />
|
||||
<option phantom="false" />
|
||||
<option plusplus="false" />
|
||||
<option proto="false" />
|
||||
<option prototypejs="false" />
|
||||
<option qunit="false" />
|
||||
<option quotmark="false" />
|
||||
<option rhino="false" />
|
||||
<option scripturl="false" />
|
||||
<option shadow="false" />
|
||||
<option shelljs="false" />
|
||||
<option singleGroups="false" />
|
||||
<option smarttabs="false" />
|
||||
<option strict="true" />
|
||||
<option sub="false" />
|
||||
<option supernew="false" />
|
||||
<option trailing="false" />
|
||||
<option typed="false" />
|
||||
<option undef="true" />
|
||||
<option unused="false" />
|
||||
<option validthis="false" />
|
||||
<option varstmt="false" />
|
||||
<option white="false" />
|
||||
<option withstmt="false" />
|
||||
<option worker="false" />
|
||||
<option wsh="false" />
|
||||
<option yui="false" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/laravel-plugin.xml
generated
Normal file
6
.idea/laravel-plugin.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="LaravelPluginSettings">
|
||||
<option name="pluginEnabled" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
</project>
|
||||
147
.idea/mivita.care.iml
generated
Normal file
147
.idea/mivita.care.iml
generated
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Tests\" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/askedio/laravel5-profanity-filter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/laravel-debugbar" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/laravel-dompdf" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/laravel-ide-helper" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/reflection-docblock" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/cocur/slugify" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/cviebrock/eloquent-sluggable" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/defuse/php-encryption" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/dnoegel/php-xdg-base-dir" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/dbal" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/event-manager" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/inflector" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/lexer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/dompdf/dompdf" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/dragonmantank/cron-expression" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/egulias/email-validator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/fideloper/proxy" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/firebase/php-jwt" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/fzaninotto/faker" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/hamcrest/hamcrest-php" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/intervention/image" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/jakub-onderka/php-console-color" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/jakub-onderka/php-console-highlighter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/jenssegers/date" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/justinrainbow/json-schema" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laminas/laminas-diactoros" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laminas/laminas-zendframework-bridge" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laracasts/flash" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/framework" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/helpers" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/passport" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/tinker" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravelcollective/html" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/lcobucci/jwt" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/commonmark" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/event" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/flysystem" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/oauth2-server" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/maatwebsite/excel" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/complex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/matrix" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/maximebf/debugbar" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/mockery/mockery" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nesbot/carbon" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/php-parser" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nyholm/psr7" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/olimortimer/laravelshoppingcart" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/opis/closure" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phenx/php-font-lib" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phenx/php-svg-lib" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/message-factory" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoffice/phpspreadsheet" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoption/phpoption" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpseclib/phpseclib" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-invoker" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psy/psysh" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/reliese/laravel" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sabberworm/php-css-parser" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/type" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/seld/jsonlint" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/seld/phar-utils" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/setasign/fpdf" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/setasign/fpdi" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/css-selector" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/debug" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/error-handler" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/filesystem" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/process" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/psr-http-message-bridge" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-dumper" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/vlucas/phpdotenv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/yajra/laravel-datatables-oracle" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/mivita.care.iml" filepath="$PROJECT_DIR$/.idea/mivita.care.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/php-test-framework.xml
generated
Normal file
14
.idea/php-test-framework.xml
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PhpTestFrameworkVersionCache">
|
||||
<tools_cache>
|
||||
<tool tool_name="PHPUnit">
|
||||
<cache>
|
||||
<versions>
|
||||
<info id="Local" version="9.1.4" />
|
||||
</versions>
|
||||
</cache>
|
||||
</tool>
|
||||
</tools_cache>
|
||||
</component>
|
||||
</project>
|
||||
148
.idea/php.xml
generated
Normal file
148
.idea/php.xml
generated
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PhpIncludePathManager">
|
||||
<include_path>
|
||||
<path value="$PROJECT_DIR$/vendor/mockery/mockery" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
|
||||
<path value="$PROJECT_DIR$/vendor/seld/phar-utils" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-token-stream" />
|
||||
<path value="$PROJECT_DIR$/vendor/composer" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/reflection-docblock" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/laravel-ide-helper" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpspec/prophecy" />
|
||||
<path value="$PROJECT_DIR$/vendor/jakub-onderka/php-console-highlighter" />
|
||||
<path value="$PROJECT_DIR$/vendor/jenssegers/date" />
|
||||
<path value="$PROJECT_DIR$/vendor/jakub-onderka/php-console-color" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/flysystem" />
|
||||
<path value="$PROJECT_DIR$/vendor/fideloper/proxy" />
|
||||
<path value="$PROJECT_DIR$/vendor/vlucas/phpdotenv" />
|
||||
<path value="$PROJECT_DIR$/vendor/psy/psysh" />
|
||||
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/debug" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/console" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/process" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/http-kernel" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/filesystem" />
|
||||
<path value="$PROJECT_DIR$/vendor/seld/jsonlint" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/css-selector" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/dbal" />
|
||||
<path value="$PROJECT_DIR$/vendor/swiftmailer/swiftmailer" />
|
||||
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<path value="$PROJECT_DIR$/vendor/justinrainbow/json-schema" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
|
||||
<path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/log" />
|
||||
<path value="$PROJECT_DIR$/vendor/dnoegel/php-xdg-base-dir" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/container" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
|
||||
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/tinker" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/framework" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravelcollective/html" />
|
||||
<path value="$PROJECT_DIR$/vendor/egulias/email-validator" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
|
||||
<path value="$PROJECT_DIR$/vendor/filp/whoops" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
|
||||
<path value="$PROJECT_DIR$/vendor/intervention/image" />
|
||||
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
|
||||
<path value="$PROJECT_DIR$/vendor/yajra/laravel-datatables-oracle" />
|
||||
<path value="$PROJECT_DIR$/vendor/fzaninotto/faker" />
|
||||
<path value="$PROJECT_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
|
||||
<path value="$PROJECT_DIR$/vendor/monolog/monolog" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/event-manager" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/inflector" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/cache" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
|
||||
<path value="$PROJECT_DIR$/vendor/ramsey/uuid" />
|
||||
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/lexer" />
|
||||
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
|
||||
<path value="$PROJECT_DIR$/vendor/nunomaduro/collision" />
|
||||
<path value="$PROJECT_DIR$/vendor/nesbot/carbon" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/type-resolver" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
|
||||
<path value="$PROJECT_DIR$/vendor/hamcrest/hamcrest-php" />
|
||||
<path value="$PROJECT_DIR$/vendor/dragonmantank/cron-expression" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" />
|
||||
<path value="$PROJECT_DIR$/vendor/cviebrock/eloquent-sluggable" />
|
||||
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
|
||||
<path value="$PROJECT_DIR$/vendor/askedio/laravel5-profanity-filter" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/error-handler" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-iconv" />
|
||||
<path value="$PROJECT_DIR$/vendor/reliese/laravel" />
|
||||
<path value="$PROJECT_DIR$/vendor/setasign/fpdf" />
|
||||
<path value="$PROJECT_DIR$/vendor/setasign/fpdi" />
|
||||
<path value="$PROJECT_DIR$/vendor/olimortimer/laravelshoppingcart" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/commonmark" />
|
||||
<path value="$PROJECT_DIR$/vendor/opis/closure" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpoption/phpoption" />
|
||||
<path value="$PROJECT_DIR$/vendor/maximebf/debugbar" />
|
||||
<path value="$PROJECT_DIR$/vendor/laracasts/flash" />
|
||||
<path value="$PROJECT_DIR$/vendor/cocur/slugify" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/laravel-debugbar" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
|
||||
<path value="$PROJECT_DIR$/vendor/laminas/laminas-diactoros" />
|
||||
<path value="$PROJECT_DIR$/vendor/laminas/laminas-zendframework-bridge" />
|
||||
<path value="$PROJECT_DIR$/vendor/dompdf/dompdf" />
|
||||
<path value="$PROJECT_DIR$/vendor/php-http/message-factory" />
|
||||
<path value="$PROJECT_DIR$/vendor/lcobucci/jwt" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/passport" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/helpers" />
|
||||
<path value="$PROJECT_DIR$/vendor/nyholm/psr7" />
|
||||
<path value="$PROJECT_DIR$/vendor/phenx/php-font-lib" />
|
||||
<path value="$PROJECT_DIR$/vendor/phenx/php-svg-lib" />
|
||||
<path value="$PROJECT_DIR$/vendor/defuse/php-encryption" />
|
||||
<path value="$PROJECT_DIR$/vendor/firebase/php-jwt" />
|
||||
<path value="$PROJECT_DIR$/vendor/sabberworm/php-css-parser" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpseclib/phpseclib" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/event" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/oauth2-server" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/laravel-dompdf" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
|
||||
<path value="$PROJECT_DIR$/vendor/maatwebsite/excel" />
|
||||
<path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
|
||||
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
|
||||
<component name="PhpUnit">
|
||||
<phpunit_settings>
|
||||
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
|
||||
</phpunit_settings>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/symfony2.xml
generated
Normal file
8
.idea/symfony2.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Symfony2PluginSettings">
|
||||
<option name="directoryToWeb" value="public" />
|
||||
<option name="pluginEnabled" value="true" />
|
||||
<option name="lastServiceGeneratorLanguage" value="yaml" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
627
.idea/workspace.xml
generated
Normal file
627
.idea/workspace.xml
generated
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="2fbaac5f-25ba-4502-a970-cc14728d7d55" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/resources/views/user/shop/sales/order_detail.blade.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Http/Controllers/Api/ShoppingUserController.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/Api/ShoppingUserController.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Http/Controllers/ProductController.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/ProductController.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Http/Controllers/SalesController.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/SalesController.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Http/Controllers/Web/ContactController.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/Web/ContactController.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Mail/MailCheckout.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Mail/MailCheckout.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Models/ShippingPrice.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Models/ShippingPrice.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Models/ShoppingOrder.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Models/ShoppingOrder.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Models/ShoppingUser.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Models/ShoppingUser.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/Repositories/ProductRepository.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Repositories/ProductRepository.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config/cart.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/cart.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config/mail.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/mail.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/database/migrations/2019_02_23_163527_create_shopping_orders_table.php" beforeDir="false" afterPath="$PROJECT_DIR$/database/migrations/2019_02_23_163527_create_shopping_orders_table.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/lang/de Kopie.json" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/lang/de/email.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/lang/de/email.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/lang/de/register.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/lang/de/register.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/views/admin/product/index.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/admin/product/index.blade.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/views/admin/sales/_detail.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/admin/sales/_detail.blade.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/views/emails/checkout.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/emails/checkout.blade.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/views/layouts/application.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/layouts/application.blade.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/views/user/customer/detail.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/user/customer/detail.blade.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/resources/views/web/templates/registrierung.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/web/templates/registrierung.blade.php" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/routes/api.php" beforeDir="false" afterPath="$PROJECT_DIR$/routes/api.php" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ComposerSettings" doNotAsk="true" synchronizationState="SYNCHRONIZE">
|
||||
<pharConfigPath>$PROJECT_DIR$/composer.json</pharConfigPath>
|
||||
<execution>
|
||||
<phar pharPath="/usr/local/bin/composer" interpreterId="1b9e99f0-0fb5-45bb-9686-0d9b61bf499b" />
|
||||
</execution>
|
||||
</component>
|
||||
<component name="DatabaseView">
|
||||
<option name="SHOW_INTERMEDIATE" value="true" />
|
||||
<option name="GROUP_DATA_SOURCES" value="true" />
|
||||
<option name="GROUP_SCHEMA" value="true" />
|
||||
<option name="GROUP_CONTENTS" value="false" />
|
||||
<option name="SORT_POSITIONED" value="false" />
|
||||
<option name="SHOW_EMPTY_GROUPS" value="false" />
|
||||
<option name="AUTO_SCROLL_FROM_SOURCE" value="false" />
|
||||
<option name="HIDDEN_KINDS">
|
||||
<set />
|
||||
</option>
|
||||
<expand>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="MySQL - @localhost" type="feb32156:DbDataSourceImpl" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="MySQL - @localhost" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="MySQL - @localhost" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||
<item name="mivita: schema" type="76f4a085:MysqlImplModel$Schema" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="MySQL - @localhost" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||
<item name="mivita: schema" type="76f4a085:MysqlImplModel$Schema" />
|
||||
<item name="user_histories: table" type="285a2a93:MysqlImplModel$Table" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</component>
|
||||
<component name="FrameworkCommandLineHistory">
|
||||
<commandsHistory>
|
||||
<command text="php artisan ide-helper:meta" />
|
||||
<command text="php artisan ide-helper:models" />
|
||||
<command text="php artisan ide-helper:generate" />
|
||||
<command text="php artisan ide-helper:models" />
|
||||
<command text="php artisan ide-helper:meta" />
|
||||
<command text="php artisan ide-helper:models" />
|
||||
<command text="php artisan ide-helper:generate" />
|
||||
<command text="php artisan ide-helper:models" />
|
||||
<command text="php artisan ide-helper:generate" />
|
||||
<command text="composer dump-autoload" />
|
||||
<command text="composer run-script post-update-cmd" />
|
||||
<command text="php artisan ide-helper:models" />
|
||||
<command text="php artisan ide-helper:meta" />
|
||||
<command text="php artisan ide-helper:generate" />
|
||||
<command text="php artisan ide-helper:meta" />
|
||||
<command text="php artisan ide-helper:models" />
|
||||
<command text="php artisan ide-helper:generate" />
|
||||
<command text="composer dump-autoload" />
|
||||
<command text="composer run-script post-update-cmd" />
|
||||
<command text="php artisan ide-helper:generate" />
|
||||
<command text="composer run-script post-update-cmd" />
|
||||
<command text="php artisan ide-helper:models" />
|
||||
<command text="php artisan ide-helper:meta" />
|
||||
<command text="php artisan ide-helper:generate" />
|
||||
<command text="composer run-script post-update-cmd" />
|
||||
</commandsHistory>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="GitSEFilterConfiguration">
|
||||
<file-type-list>
|
||||
<filtered-out-file-type name="LOCAL_BRANCH" />
|
||||
<filtered-out-file-type name="REMOTE_BRANCH" />
|
||||
<filtered-out-file-type name="TAG" />
|
||||
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
|
||||
</file-type-list>
|
||||
</component>
|
||||
<component name="PhpServers">
|
||||
<servers />
|
||||
</component>
|
||||
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="PHP 7.1">
|
||||
<include_path>
|
||||
<path value="$PROJECT_DIR$/vendor/mockery/mockery" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
|
||||
<path value="$PROJECT_DIR$/vendor/seld/phar-utils" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-token-stream" />
|
||||
<path value="$PROJECT_DIR$/vendor/composer" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/reflection-docblock" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/laravel-ide-helper" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpspec/prophecy" />
|
||||
<path value="$PROJECT_DIR$/vendor/jakub-onderka/php-console-highlighter" />
|
||||
<path value="$PROJECT_DIR$/vendor/jenssegers/date" />
|
||||
<path value="$PROJECT_DIR$/vendor/jakub-onderka/php-console-color" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/flysystem" />
|
||||
<path value="$PROJECT_DIR$/vendor/fideloper/proxy" />
|
||||
<path value="$PROJECT_DIR$/vendor/vlucas/phpdotenv" />
|
||||
<path value="$PROJECT_DIR$/vendor/psy/psysh" />
|
||||
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/debug" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/console" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/process" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/http-kernel" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/filesystem" />
|
||||
<path value="$PROJECT_DIR$/vendor/seld/jsonlint" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/css-selector" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/dbal" />
|
||||
<path value="$PROJECT_DIR$/vendor/swiftmailer/swiftmailer" />
|
||||
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<path value="$PROJECT_DIR$/vendor/justinrainbow/json-schema" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
|
||||
<path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/log" />
|
||||
<path value="$PROJECT_DIR$/vendor/dnoegel/php-xdg-base-dir" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/container" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
|
||||
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/tinker" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/framework" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravelcollective/html" />
|
||||
<path value="$PROJECT_DIR$/vendor/egulias/email-validator" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
|
||||
<path value="$PROJECT_DIR$/vendor/filp/whoops" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
|
||||
<path value="$PROJECT_DIR$/vendor/intervention/image" />
|
||||
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
|
||||
<path value="$PROJECT_DIR$/vendor/yajra/laravel-datatables-oracle" />
|
||||
<path value="$PROJECT_DIR$/vendor/fzaninotto/faker" />
|
||||
<path value="$PROJECT_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
|
||||
<path value="$PROJECT_DIR$/vendor/monolog/monolog" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/event-manager" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/inflector" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/cache" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
|
||||
<path value="$PROJECT_DIR$/vendor/ramsey/uuid" />
|
||||
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/lexer" />
|
||||
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
|
||||
<path value="$PROJECT_DIR$/vendor/nunomaduro/collision" />
|
||||
<path value="$PROJECT_DIR$/vendor/nesbot/carbon" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/type-resolver" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
|
||||
<path value="$PROJECT_DIR$/vendor/hamcrest/hamcrest-php" />
|
||||
<path value="$PROJECT_DIR$/vendor/dragonmantank/cron-expression" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" />
|
||||
<path value="$PROJECT_DIR$/vendor/cviebrock/eloquent-sluggable" />
|
||||
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
|
||||
<path value="$PROJECT_DIR$/vendor/askedio/laravel5-profanity-filter" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/error-handler" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-iconv" />
|
||||
<path value="$PROJECT_DIR$/vendor/reliese/laravel" />
|
||||
<path value="$PROJECT_DIR$/vendor/setasign/fpdf" />
|
||||
<path value="$PROJECT_DIR$/vendor/setasign/fpdi" />
|
||||
<path value="$PROJECT_DIR$/vendor/olimortimer/laravelshoppingcart" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/commonmark" />
|
||||
<path value="$PROJECT_DIR$/vendor/opis/closure" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpoption/phpoption" />
|
||||
<path value="$PROJECT_DIR$/vendor/maximebf/debugbar" />
|
||||
<path value="$PROJECT_DIR$/vendor/laracasts/flash" />
|
||||
<path value="$PROJECT_DIR$/vendor/cocur/slugify" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/laravel-debugbar" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
|
||||
<path value="$PROJECT_DIR$/vendor/laminas/laminas-diactoros" />
|
||||
<path value="$PROJECT_DIR$/vendor/laminas/laminas-zendframework-bridge" />
|
||||
<path value="$PROJECT_DIR$/vendor/dompdf/dompdf" />
|
||||
<path value="$PROJECT_DIR$/vendor/php-http/message-factory" />
|
||||
<path value="$PROJECT_DIR$/vendor/lcobucci/jwt" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/passport" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/helpers" />
|
||||
<path value="$PROJECT_DIR$/vendor/nyholm/psr7" />
|
||||
<path value="$PROJECT_DIR$/vendor/phenx/php-font-lib" />
|
||||
<path value="$PROJECT_DIR$/vendor/phenx/php-svg-lib" />
|
||||
<path value="$PROJECT_DIR$/vendor/defuse/php-encryption" />
|
||||
<path value="$PROJECT_DIR$/vendor/firebase/php-jwt" />
|
||||
<path value="$PROJECT_DIR$/vendor/sabberworm/php-css-parser" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpseclib/phpseclib" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/event" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/oauth2-server" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
|
||||
<path value="$PROJECT_DIR$/vendor/barryvdh/laravel-dompdf" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
|
||||
<path value="$PROJECT_DIR$/vendor/maatwebsite/excel" />
|
||||
<path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
|
||||
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="ProjectId" id="1Ply2ASLtUbE38LpjzejPOnh0np" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
|
||||
<ConfirmationsSetting value="2" id="Add" />
|
||||
</component>
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
|
||||
<property name="DatabaseDriversLRU" value="mysql" />
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/resources/views/user/shop/sales" />
|
||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
||||
<property name="nodejs_package_manager_path" value="npm" />
|
||||
<property name="settings.editor.selected.configurable" value="preferences.pluginManager" />
|
||||
<property name="vue.rearranger.settings.migration" value="true" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/resources/views/user/shop/sales" />
|
||||
<recent name="$PROJECT_DIR$/resources/views/emails" />
|
||||
<recent name="$PROJECT_DIR$/resources/views/admin/sales" />
|
||||
<recent name="$PROJECT_DIR$/resources/views/user/homeparty" />
|
||||
<recent name="$PROJECT_DIR$/app/Services" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/resources/views/user/order" />
|
||||
<recent name="$PROJECT_DIR$/resources/views/user/customer" />
|
||||
<recent name="$PROJECT_DIR$/public/vendor/libs/dropzone" />
|
||||
<recent name="$PROJECT_DIR$/public/vendor/libs" />
|
||||
<recent name="$PROJECT_DIR$/resources/views/admin/user" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="Shell Script.composer-post-update.sh">
|
||||
<configuration name="de" type="PHPUnitRunConfigurationType" factoryName="PHPUnit">
|
||||
<TestRunner directory="$PROJECT_DIR$/resources/lang/de" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration default="true" type="ShConfigurationType">
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="$USER_HOME$/Documents/scripts/composer-post-update.sh" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="/bin/bash" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="composer-post-update.sh" type="ShConfigurationType">
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="$USER_HOME$/Documents/scripts/composer-post-update.sh" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="/bin/bash" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="2fbaac5f-25ba-4502-a970-cc14728d7d55" name="Default Changelist" comment="" />
|
||||
<created>1537353993493</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1537353993493</updated>
|
||||
<workItem from="1537353995669" duration="3800000" />
|
||||
<workItem from="1540127177718" duration="362000" />
|
||||
<workItem from="1540127554029" duration="2000" />
|
||||
<workItem from="1540127581792" duration="7784000" />
|
||||
<workItem from="1542210149879" duration="9736000" />
|
||||
<workItem from="1542297086254" duration="9567000" />
|
||||
<workItem from="1542406540801" duration="9615000" />
|
||||
<workItem from="1542727683895" duration="12063000" />
|
||||
<workItem from="1542787061822" duration="2626000" />
|
||||
<workItem from="1542806743382" duration="5489000" />
|
||||
<workItem from="1542890756312" duration="12280000" />
|
||||
<workItem from="1543070248579" duration="2602000" />
|
||||
<workItem from="1543233637816" duration="18000" />
|
||||
<workItem from="1544822014608" duration="4694000" />
|
||||
<workItem from="1544829993268" duration="5406000" />
|
||||
<workItem from="1546523595056" duration="30055000" />
|
||||
<workItem from="1546704135920" duration="19864000" />
|
||||
<workItem from="1546806383318" duration="9637000" />
|
||||
<workItem from="1547205813997" duration="946000" />
|
||||
<workItem from="1547208157542" duration="108000" />
|
||||
<workItem from="1547214977808" duration="3425000" />
|
||||
<workItem from="1547480630058" duration="1000" />
|
||||
<workItem from="1547484887720" duration="1609000" />
|
||||
<workItem from="1547540787341" duration="1640000" />
|
||||
<workItem from="1547724573711" duration="736000" />
|
||||
<workItem from="1548058316947" duration="24674000" />
|
||||
<workItem from="1548143988826" duration="810000" />
|
||||
<workItem from="1548231704434" duration="1070000" />
|
||||
<workItem from="1548773013181" duration="1000" />
|
||||
<workItem from="1549468946720" duration="8000" />
|
||||
<workItem from="1550313918368" duration="329000" />
|
||||
<workItem from="1550314558764" duration="1097000" />
|
||||
<workItem from="1550498022950" duration="3845000" />
|
||||
<workItem from="1550674465433" duration="14138000" />
|
||||
<workItem from="1550736184159" duration="1000" />
|
||||
<workItem from="1550765848683" duration="10855000" />
|
||||
<workItem from="1550838986113" duration="11950000" />
|
||||
<workItem from="1550935201261" duration="6226000" />
|
||||
<workItem from="1551181680267" duration="14089000" />
|
||||
<workItem from="1551261197445" duration="32000" />
|
||||
<workItem from="1551277131077" duration="11095000" />
|
||||
<workItem from="1551354000907" duration="15584000" />
|
||||
<workItem from="1551429281485" duration="275000" />
|
||||
<workItem from="1551429580227" duration="2056000" />
|
||||
<workItem from="1551436264383" duration="33071000" />
|
||||
<workItem from="1551522312534" duration="3206000" />
|
||||
<workItem from="1551526048926" duration="2305000" />
|
||||
<workItem from="1551530777330" duration="3050000" />
|
||||
<workItem from="1551712620909" duration="2498000" />
|
||||
<workItem from="1551962594785" duration="3948000" />
|
||||
<workItem from="1552133587503" duration="961000" />
|
||||
<workItem from="1552134613344" duration="6000" />
|
||||
<workItem from="1552135056549" duration="494000" />
|
||||
<workItem from="1552403932079" duration="1027000" />
|
||||
<workItem from="1552405331969" duration="881000" />
|
||||
<workItem from="1552473972131" duration="2992000" />
|
||||
<workItem from="1552477317174" duration="722000" />
|
||||
<workItem from="1552481695438" duration="231000" />
|
||||
<workItem from="1552482684973" duration="1481000" />
|
||||
<workItem from="1552485686862" duration="497000" />
|
||||
<workItem from="1553078315005" duration="42000" />
|
||||
<workItem from="1553172339375" duration="754000" />
|
||||
<workItem from="1553174858066" duration="22000" />
|
||||
<workItem from="1553175700548" duration="116000" />
|
||||
<workItem from="1553176149267" duration="34000" />
|
||||
<workItem from="1553687920206" duration="395000" />
|
||||
<workItem from="1553692408844" duration="6000" />
|
||||
<workItem from="1553694116510" duration="350000" />
|
||||
<workItem from="1553694855934" duration="1157000" />
|
||||
<workItem from="1553708521909" duration="492000" />
|
||||
<workItem from="1555429292695" duration="630000" />
|
||||
<workItem from="1555490678820" duration="3921000" />
|
||||
<workItem from="1556891567027" duration="851000" />
|
||||
<workItem from="1560255951776" duration="539000" />
|
||||
<workItem from="1561993809455" duration="29000" />
|
||||
<workItem from="1566462771075" duration="8798000" />
|
||||
<workItem from="1566975548985" duration="1435000" />
|
||||
<workItem from="1568115248448" duration="7659000" />
|
||||
<workItem from="1573475266384" duration="174000" />
|
||||
<workItem from="1573475447829" duration="81000" />
|
||||
<workItem from="1573480792424" duration="1748000" />
|
||||
<workItem from="1573806280308" duration="772000" />
|
||||
<workItem from="1574173803219" duration="19000" />
|
||||
<workItem from="1574495107293" duration="2126000" />
|
||||
<workItem from="1574498597084" duration="18390000" />
|
||||
<workItem from="1574870171463" duration="442000" />
|
||||
<workItem from="1575135233364" duration="233000" />
|
||||
<workItem from="1577966622446" duration="15215000" />
|
||||
<workItem from="1578042899454" duration="15408000" />
|
||||
<workItem from="1578069761418" duration="55000" />
|
||||
<workItem from="1578070274627" duration="66000" />
|
||||
<workItem from="1578493675622" duration="10000" />
|
||||
<workItem from="1578493690816" duration="11299000" />
|
||||
<workItem from="1578672302747" duration="1742000" />
|
||||
<workItem from="1578831425036" duration="6019000" />
|
||||
<workItem from="1578928076715" duration="559000" />
|
||||
<workItem from="1579028224750" duration="42000" />
|
||||
<workItem from="1580139983493" duration="1049000" />
|
||||
<workItem from="1581156718221" duration="27110000" />
|
||||
<workItem from="1581245547669" duration="5492000" />
|
||||
<workItem from="1581502538849" duration="2174000" />
|
||||
<workItem from="1581517312628" duration="9759000" />
|
||||
<workItem from="1581604452585" duration="5853000" />
|
||||
<workItem from="1581686945791" duration="4486000" />
|
||||
<workItem from="1581691515338" duration="19033000" />
|
||||
<workItem from="1582045337297" duration="7000" />
|
||||
<workItem from="1582975911467" duration="18419000" />
|
||||
<workItem from="1583498764097" duration="142000" />
|
||||
<workItem from="1583572250584" duration="19295000" />
|
||||
<workItem from="1583743438432" duration="10157000" />
|
||||
<workItem from="1583757303144" duration="7000" />
|
||||
<workItem from="1583758271548" duration="2118000" />
|
||||
<workItem from="1584782149779" duration="14772000" />
|
||||
<workItem from="1584954914592" duration="54000" />
|
||||
<workItem from="1584965547046" duration="694000" />
|
||||
<workItem from="1585396827913" duration="1986000" />
|
||||
<workItem from="1585400638388" duration="9592000" />
|
||||
<workItem from="1585576417290" duration="2940000" />
|
||||
<workItem from="1585666412873" duration="6324000" />
|
||||
<workItem from="1585729859738" duration="21452000" />
|
||||
<workItem from="1585760254632" duration="571000" />
|
||||
<workItem from="1587107320088" duration="1592000" />
|
||||
<workItem from="1587109717986" duration="1617000" />
|
||||
<workItem from="1587749609220" duration="1211000" />
|
||||
<workItem from="1587975636191" duration="17000" />
|
||||
<workItem from="1588146697363" duration="6072000" />
|
||||
<workItem from="1588153881658" duration="6306000" />
|
||||
<workItem from="1588167994966" duration="10515000" />
|
||||
<workItem from="1588231371983" duration="3000" />
|
||||
<workItem from="1588233321074" duration="6060000" />
|
||||
<workItem from="1588244040497" duration="547000" />
|
||||
<workItem from="1588244809449" duration="10424000" />
|
||||
<workItem from="1588267918069" duration="1856000" />
|
||||
<workItem from="1588410019835" duration="194000" />
|
||||
<workItem from="1588411442653" duration="11498000" />
|
||||
<workItem from="1588427157422" duration="5582000" />
|
||||
<workItem from="1588577728345" duration="8457000" />
|
||||
<workItem from="1588597117898" duration="3955000" />
|
||||
<workItem from="1588604782047" duration="6478000" />
|
||||
<workItem from="1588667412409" duration="3521000" />
|
||||
<workItem from="1588753513112" duration="331000" />
|
||||
<workItem from="1588754727146" duration="6339000" />
|
||||
<workItem from="1588761249067" duration="27000" />
|
||||
<workItem from="1588761617312" duration="1790000" />
|
||||
<workItem from="1588771915210" duration="5000" />
|
||||
<workItem from="1588778353962" duration="35000" />
|
||||
<workItem from="1588779256522" duration="57000" />
|
||||
<workItem from="1588921253853" duration="2440000" />
|
||||
<workItem from="1589023916958" duration="245000" />
|
||||
<workItem from="1589355379810" duration="20000" />
|
||||
<workItem from="1589365636618" duration="4232000" />
|
||||
<workItem from="1589442905154" duration="21886000" />
|
||||
<workItem from="1589474488991" duration="3220000" />
|
||||
<workItem from="1589526578461" duration="28000" />
|
||||
<workItem from="1589881416788" duration="7714000" />
|
||||
<workItem from="1589891829781" duration="681000" />
|
||||
<workItem from="1589892673376" duration="9169000" />
|
||||
<workItem from="1589902453020" duration="273000" />
|
||||
<workItem from="1589974449247" duration="240000" />
|
||||
<workItem from="1590758073413" duration="5869000" />
|
||||
<workItem from="1590826972537" duration="2356000" />
|
||||
<workItem from="1590829892402" duration="8158000" />
|
||||
<workItem from="1591187714339" duration="7341000" />
|
||||
<workItem from="1591361845658" duration="2000" />
|
||||
<workItem from="1591619467115" duration="326000" />
|
||||
<workItem from="1591864110935" duration="4671000" />
|
||||
<workItem from="1591868858012" duration="15798000" />
|
||||
<workItem from="1591889648864" duration="8510000" />
|
||||
<workItem from="1591959307471" duration="5000" />
|
||||
<workItem from="1591959326440" duration="5517000" />
|
||||
<workItem from="1592132838802" duration="47000" />
|
||||
<workItem from="1592134604562" duration="347000" />
|
||||
<workItem from="1592134995936" duration="7000" />
|
||||
<workItem from="1592135066330" duration="179000" />
|
||||
<workItem from="1592831744708" duration="9000" />
|
||||
<workItem from="1592831761021" duration="3000" />
|
||||
<workItem from="1592905155133" duration="2081000" />
|
||||
<workItem from="1593539130650" duration="5379000" />
|
||||
<workItem from="1593607978036" duration="4673000" />
|
||||
<workItem from="1594295945600" duration="612000" />
|
||||
<workItem from="1594721064087" duration="5912000" />
|
||||
<workItem from="1594733233309" duration="1305000" />
|
||||
<workItem from="1595674257808" duration="9321000" />
|
||||
<workItem from="1595923785716" duration="1023000" />
|
||||
<workItem from="1595948227650" duration="1138000" />
|
||||
<workItem from="1595949374319" duration="4908000" />
|
||||
<workItem from="1596107641527" duration="35000" />
|
||||
<workItem from="1596107688024" duration="1943000" />
|
||||
<workItem from="1596111667880" duration="6240000" />
|
||||
<workItem from="1596119318253" duration="10939000" />
|
||||
<workItem from="1596188575338" duration="7868000" />
|
||||
<workItem from="1596268902765" duration="165000" />
|
||||
<workItem from="1596269074069" duration="69000" />
|
||||
<workItem from="1596802450904" duration="6190000" />
|
||||
<workItem from="1596873885106" duration="22962000" />
|
||||
<workItem from="1597138806746" duration="5000" />
|
||||
<workItem from="1597216110022" duration="32246000" />
|
||||
<workItem from="1597299830339" duration="2158000" />
|
||||
<workItem from="1597313688277" duration="5656000" />
|
||||
<workItem from="1597410738317" duration="11162000" />
|
||||
<workItem from="1597658985953" duration="2000" />
|
||||
<workItem from="1597928402978" duration="7140000" />
|
||||
<workItem from="1598016608485" duration="9648000" />
|
||||
<workItem from="1598255562481" duration="22043000" />
|
||||
<workItem from="1598282618629" duration="1154000" />
|
||||
<workItem from="1598434849256" duration="514000" />
|
||||
<workItem from="1598971116331" duration="4130000" />
|
||||
<workItem from="1599033832974" duration="4000" />
|
||||
<workItem from="1599558681455" duration="23060000" />
|
||||
<workItem from="1599633110239" duration="250000" />
|
||||
<workItem from="1600079440472" duration="408000" />
|
||||
<workItem from="1600096831480" duration="2472000" />
|
||||
<workItem from="1600175774205" duration="25000" />
|
||||
<workItem from="1600179917484" duration="407000" />
|
||||
<workItem from="1600328859516" duration="262000" />
|
||||
<workItem from="1602762026312" duration="11122000" />
|
||||
<workItem from="1602831799597" duration="12765000" />
|
||||
<workItem from="1602854134928" duration="3330000" />
|
||||
<workItem from="1602857574059" duration="218000" />
|
||||
<workItem from="1603107642331" duration="5950000" />
|
||||
<workItem from="1603264695225" duration="33000" />
|
||||
<workItem from="1603272469212" duration="17000" />
|
||||
<workItem from="1603730718098" duration="118000" />
|
||||
<workItem from="1603985533656" duration="12738000" />
|
||||
<workItem from="1604392770203" duration="27000" />
|
||||
<workItem from="1604419165739" duration="43000" />
|
||||
<workItem from="1604503127063" duration="71000" />
|
||||
<workItem from="1604507908338" duration="28000" />
|
||||
<workItem from="1604507968294" duration="8000" />
|
||||
<workItem from="1604921993804" duration="1271000" />
|
||||
<workItem from="1604923532802" duration="515000" />
|
||||
<workItem from="1604939815128" duration="6462000" />
|
||||
<workItem from="1605006776997" duration="3000" />
|
||||
<workItem from="1605201129884" duration="22000" />
|
||||
<workItem from="1605201310540" duration="157000" />
|
||||
<workItem from="1605599347354" duration="281000" />
|
||||
<workItem from="1608113166691" duration="26421000" />
|
||||
<workItem from="1608198795441" duration="30640000" />
|
||||
<workItem from="1608482656314" duration="1180000" />
|
||||
<workItem from="1609845987050" duration="8455000" />
|
||||
<workItem from="1609856766161" duration="7420000" />
|
||||
<workItem from="1609921488733" duration="11747000" />
|
||||
<workItem from="1609951522474" duration="3068000" />
|
||||
<workItem from="1610017442331" duration="599000" />
|
||||
<workItem from="1610105883226" duration="2999000" />
|
||||
<workItem from="1610111682841" duration="993000" />
|
||||
<workItem from="1610113229757" duration="58000" />
|
||||
<workItem from="1610114243791" duration="73000" />
|
||||
<workItem from="1610708369185" duration="36000" />
|
||||
<workItem from="1610709005944" duration="206000" />
|
||||
<workItem from="1610709247428" duration="439000" />
|
||||
<workItem from="1610718553130" duration="104000" />
|
||||
<workItem from="1610729780780" duration="23000" />
|
||||
<workItem from="1610990236245" duration="284000" />
|
||||
<workItem from="1611071419100" duration="35000" />
|
||||
<workItem from="1611138964858" duration="11000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TodoView">
|
||||
<todo-panel id="selected-file">
|
||||
<is-autoscroll-to-source value="true" />
|
||||
</todo-panel>
|
||||
<todo-panel id="all">
|
||||
<are-packages-shown value="true" />
|
||||
<is-autoscroll-to-source value="true" />
|
||||
</todo-panel>
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
<entry key="MAIN">
|
||||
<value>
|
||||
<State />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="oldMeFiltersMigrated" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
27
.mcp.json
27
.mcp.json
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
5690
.phpstorm.meta.php
5690
.phpstorm.meta.php
File diff suppressed because it is too large
Load diff
|
|
@ -1 +1 @@
|
|||
{"version":1,"defects":{"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_uses_database_locking_for_invoice_numbers":5,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_prevents_double_processing_of_same_payment":8},"times":{"Tests\\Unit\\Services\\InvoiceServiceTest::it_gets_current_invoice_number":0.002,"Tests\\Unit\\Services\\InvoiceServiceTest::it_increments_invoice_number":0.002,"Tests\\Unit\\Services\\InvoiceServiceTest::it_increments_sequentially":0.004,"Tests\\Unit\\Services\\InvoiceServiceTest::it_formats_invoice_number_with_year_prefix":0.001,"Tests\\Unit\\Services\\InvoiceServiceTest::it_generates_correct_storage_paths":0.001,"Tests\\Unit\\Services\\InvoiceServiceTest::it_generates_correct_filenames":0.001,"Tests\\Unit\\Services\\InvoiceServiceTest::it_initializes_invoice_number_when_not_exists":0.002,"Tests\\Unit\\Services\\InvoiceServiceTest::it_uses_transaction_for_invoice_number_increment":0.003,"Tests\\Unit\\Services\\InvoiceServiceTest::it_locks_setting_during_increment":0.001,"Tests\\Unit\\Services\\InvoiceServiceTest::it_pads_invoice_numbers_correctly":0.001,"Tests\\Unit\\Services\\InvoiceServiceTest::it_returns_zero_when_invoice_number_not_set":0.002,"Tests\\Unit\\Services\\InvoiceServiceTest::it_handles_rapid_increments_without_gaps":0.009,"Tests\\Unit\\Services\\InvoiceServiceTest::it_returns_integer_invoice_number":0.002,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_generates_unique_invoice_numbers_under_concurrent_load":0.02,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_atomically_increments_invoice_numbers":0.004,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_does_not_skip_invoice_numbers_on_transaction_rollback":0.002,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_uses_database_locking_for_invoice_numbers":0.003,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_prevents_double_processing_of_same_payment":0.006,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_can_lock_settings_for_update":0.001,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_creates_invoice_numbers_with_correct_format":0.001,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_handles_rapid_sequential_invoice_creation":0.012,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_initializes_invoice_number_if_not_exists":0.002,"Tests\\Feature\\Payment\\ConcurrentPaymentTest::it_handles_concurrent_transaction_commits":0.003,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_locks_shopping_order_during_payment_processing":0.001,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_prevents_double_payment_processing":0.002,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_serializes_concurrent_payment_requests":0.003,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_rolls_back_payment_on_error":0.003,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_tracks_payment_status_transitions":0.003,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_handles_concurrent_payments_for_different_orders":0.003,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_isolates_payment_transactions":0.002,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_prevents_concurrent_modifications":0.001,"Tests\\Feature\\Payment\\PayoneRaceConditionTest::it_enforces_unique_payment_references":0.002}}
|
||||
C:37:"PHPUnit\Runner\DefaultTestResultCache":44:{a:2:{s:7:"defects";a:0:{}s:5:"times";a:0:{}}}
|
||||
448
CLAUDE.md
448
CLAUDE.md
|
|
@ -1,448 +0,0 @@
|
|||
# CLAUDE.md
|
||||
|
||||
## Projekt-Übersicht
|
||||
|
||||
**mivita.care** - Laravel 11 CRM für Netzwerk-Marketing (MLM) mit E-Commerce, Provisionsberechnung, DHL-Versand und Multi-Tenancy.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Komponente | Technologie |
|
||||
| ----------- | -------------------------------------------- |
|
||||
| Framework | Laravel 11 (Classic MVC) |
|
||||
| Frontend | Bootstrap 4 (Appwork Theme), Blade Templates |
|
||||
| Auth | Laravel Passport (OAuth2/API) |
|
||||
| Database | MySQL 8 |
|
||||
| Data Tables | Yajra Datatables |
|
||||
| Testing | Pest PHP |
|
||||
| Queue | Laravel Horizon + Redis |
|
||||
| PDF | barryvdh/laravel-dompdf |
|
||||
| Slugs | Eloquent-Sluggable |
|
||||
| Forms | Spatie\Html |
|
||||
|
||||
## Wichtige Regeln (Constraints)
|
||||
|
||||
1. **Kein Livewire / Keine Vue-Komponenten** - Nutze reines Blade + Bootstrap
|
||||
2. **Formulare** - Nutze `Spatie\Html` für Formular-Generierung (nicht reines HTML)
|
||||
3. **Datentabellen** - Nutze ausschließlich `Yajra\Datatables` für Listenansichten im Admin
|
||||
4. **PDF** - Rechnungen/Berichte via `barryvdh/laravel-dompdf`
|
||||
5. **SEO/Slugs** - Nutze `Eloquent-Sluggable` für Berater-Profile/Produkte
|
||||
6. **Code Style** - Immer `./vendor/bin/pint` nach Änderungen ausführen
|
||||
|
||||
## Custom Packages
|
||||
|
||||
| Package | Pfad | Beschreibung |
|
||||
| --------------- | ------------------------------------ | --------------------------- |
|
||||
| DHL Integration | `packages/acme-laravel-dhl` | Versand-Labels und Tracking |
|
||||
| Shopping Cart | `packages/digital-bird/shoppingcart` | Warenkorb-System |
|
||||
|
||||
## Environment
|
||||
|
||||
This project runs inside a **Dev Container**. Commands are executed directly with `php`, `composer`, and `npm` — **without** the `vendor/bin/sail` prefix. All `sail`-prefixed commands in docs or rules should be translated to their direct equivalents:
|
||||
|
||||
| Sail | Dev Container |
|
||||
| ------------------------------ | ------------------- |
|
||||
| `vendor/bin/sail artisan ...` | `php artisan ...` |
|
||||
| `vendor/bin/sail composer ...` | `composer ...` |
|
||||
| `vendor/bin/sail npm ...` | `npm ...` |
|
||||
| `vendor/bin/sail php ...` | `php ...` |
|
||||
| `vendor/bin/sail bin pint` | `./vendor/bin/pint` |
|
||||
|
||||
## Projekt-Struktur
|
||||
|
||||
```
|
||||
app/
|
||||
├── Console/Commands/ # Artisan Commands (Business*, User*)
|
||||
├── Cron/ # Scheduled Task Logic
|
||||
├── Http/Controllers/ # 40+ Controller
|
||||
├── Models/ # Eloquent Models
|
||||
├── Repositories/ # Data Access Layer
|
||||
├── Services/ # Business Logic
|
||||
│ └── BusinessPlan/ # MLM-Berechnungen
|
||||
packages/ # Custom Packages
|
||||
dev/ # Dokumentation
|
||||
```
|
||||
|
||||
## Wichtige Dateien
|
||||
|
||||
- **MLM-Berechnung**: `app/Services/BusinessPlan/TreeCalcBotOptimized.php`
|
||||
- **Cron Jobs**: `app/Console/Kernel.php`
|
||||
- **Domain Resolver**: Multi-Tenant Middleware
|
||||
- **Cart Systems**: AboOrderCart, HomepartyCart, ShopApiOrderCart
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Laravel Basis
|
||||
|
||||
```bash
|
||||
php artisan serve # Dev Server
|
||||
php artisan migrate # Migrations
|
||||
php artisan cache:clear # Cache leeren
|
||||
php artisan config:clear
|
||||
php artisan route:clear
|
||||
php artisan view:clear
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
./vendor/bin/pint # Code formatieren
|
||||
./vendor/bin/pest # Tests ausführen
|
||||
./vendor/bin/pest --filter=TestName # Einzelner Test
|
||||
```
|
||||
|
||||
### IDE Helper
|
||||
|
||||
```bash
|
||||
php artisan ide-helper:generate
|
||||
php artisan ide-helper:models
|
||||
php artisan ide-helper:meta
|
||||
```
|
||||
|
||||
### Assets
|
||||
|
||||
```bash
|
||||
npm install && npm run dev # Development
|
||||
npm run production # Production Build
|
||||
```
|
||||
|
||||
### Business Commands
|
||||
|
||||
```bash
|
||||
# Provisionsberechnung (empfohlen: optimierte Version)
|
||||
php artisan business:store-optimized {month} {year}
|
||||
php artisan business:store-optimized {month} {year} --clear
|
||||
|
||||
# Daten löschen
|
||||
php artisan business:clear-data {month} {year} [--force]
|
||||
|
||||
# Level Reports
|
||||
php artisan business:level-reports {month} {year}
|
||||
|
||||
# User Management
|
||||
php artisan user:cleanup
|
||||
php artisan user:make_abo_order
|
||||
|
||||
# Zahlungen
|
||||
php artisan payments:check-accounts
|
||||
|
||||
# Test Account
|
||||
php artisan business:test-account
|
||||
```
|
||||
|
||||
## Scheduled Tasks (Cron)
|
||||
|
||||
| Zeit | Command | Beschreibung |
|
||||
| ----- | ------------------------------ | -------------------- |
|
||||
| 02:00 | `payments:check-accounts` | Zahlungsprüfung |
|
||||
| 03:00 | `business:store-optimized 0 0` | Provisionsberechnung |
|
||||
| 03:30 | `user:cleanup` | User-Bereinigung |
|
||||
| 04:00 | `user:make_abo_order` | Abo-Bestellungen |
|
||||
|
||||
## Docker Environment
|
||||
|
||||
```bash
|
||||
docker-compose up -d # Services starten
|
||||
./vendor/bin/sail artisan [command] # Artisan im Container
|
||||
./vendor/bin/sail shell # Shell im Container
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
- **laravel.test** - Hauptanwendung (Traefik + SSL)
|
||||
- **horizon** - Queue Worker
|
||||
- **mysql** - MySQL 8.0
|
||||
- **redis** - Cache/Session
|
||||
- **mailpit** - Mail Testing (`mivita-mail.test`)
|
||||
|
||||
### Domains
|
||||
|
||||
- Main: `mivita.test`
|
||||
- Wildcard: `*.mivita.test`
|
||||
- Mail: `mivita-mail.test`
|
||||
|
||||
## MLM-Architektur
|
||||
|
||||
### Kernkonzepte
|
||||
|
||||
- **Consultants (Berater)**: Hierarchie mit Upline/Downline
|
||||
- **TreeCalcBotOptimized**: Provisionsberechnung für MLM-Strukturen
|
||||
- **BusinessPlan Services**: Sales Volumes, Ränge, Boni
|
||||
|
||||
### Performance
|
||||
|
||||
- Memory Monitoring in optimierten Commands
|
||||
- Automatic Garbage Collection
|
||||
- Performance Logging
|
||||
|
||||
## Hinweise für Claude
|
||||
|
||||
- Nutze immer `TreeCalcBotOptimized` für neue Business-Features
|
||||
- Prüfe Custom Packages in `packages/` vor Änderungen
|
||||
- Beachte Multi-Tenant Domain-Logik bei Routing-Änderungen
|
||||
- DHL-Integration unterstützt Sandbox/Production Mode
|
||||
|
||||
===
|
||||
|
||||
<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.4.12
|
||||
- laravel/framework (LARAVEL) - v11
|
||||
- laravel/horizon (HORIZON) - v5
|
||||
- laravel/passport (PASSPORT) - v12
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/mcp (MCP) - v0
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/sail (SAIL) - v1
|
||||
- pestphp/pest (PEST) - v2
|
||||
- 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 `vendor/bin/sail npm run build`, `vendor/bin/sail npm run dev`, or `vendor/bin/sail 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`.
|
||||
|
||||
=== sail rules ===
|
||||
|
||||
## Laravel Sail
|
||||
|
||||
- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail.
|
||||
- Start services using `vendor/bin/sail up -d` and stop them with `vendor/bin/sail stop`.
|
||||
- Open the application in the browser by running `vendor/bin/sail open`.
|
||||
- Always prefix PHP, Artisan, Composer, and Node commands with `vendor/bin/sail`. Examples:
|
||||
- Run Artisan Commands: `vendor/bin/sail artisan migrate`
|
||||
- Install Composer packages: `vendor/bin/sail composer install`
|
||||
- Execute Node commands: `vendor/bin/sail npm run dev`
|
||||
- Execute PHP scripts: `vendor/bin/sail php [script]`
|
||||
- View all available Sail commands by running `vendor/bin/sail` without arguments.
|
||||
|
||||
=== tests rules ===
|
||||
|
||||
## Test Enforcement
|
||||
|
||||
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
|
||||
- Run the minimum number of tests needed to ensure code quality and speed. Use `vendor/bin/sail artisan test --compact` with a specific filename or filter.
|
||||
|
||||
=== laravel/core rules ===
|
||||
|
||||
## Do Things the Laravel Way
|
||||
|
||||
- Use `vendor/bin/sail 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 `vendor/bin/sail 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 `vendor/bin/sail 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 `vendor/bin/sail 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 `vendor/bin/sail npm run build` or ask the user to run `vendor/bin/sail npm run dev` or `vendor/bin/sail composer run dev`.
|
||||
|
||||
=== laravel/v11 rules ===
|
||||
|
||||
## Laravel 11
|
||||
|
||||
- Use the `search-docs` tool to get version-specific documentation.
|
||||
- This project upgraded from Laravel 10 without migrating to the new streamlined Laravel 11 file structure.
|
||||
- This is **perfectly fine** and recommended by Laravel. Follow the existing structure from Laravel 10. We do not need to migrate to the Laravel 11 structure unless the user explicitly requests it.
|
||||
|
||||
### Laravel 10 Structure
|
||||
- Middleware typically lives in `app/Http/Middleware/` and service providers in `app/Providers/`.
|
||||
- There is no `bootstrap/app.php` application configuration in a Laravel 10 structure:
|
||||
- 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`
|
||||
|
||||
### Database
|
||||
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
|
||||
- Laravel 11 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
|
||||
|
||||
### Models
|
||||
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
|
||||
|
||||
### New Artisan Commands
|
||||
- List Artisan commands using Boost's MCP tool, if available. New commands available in Laravel 11:
|
||||
- `vendor/bin/sail artisan make:enum`
|
||||
- `vendor/bin/sail artisan make:class`
|
||||
- `vendor/bin/sail artisan make:interface`
|
||||
|
||||
=== pint/core rules ===
|
||||
|
||||
## Laravel Pint Code Formatter
|
||||
|
||||
- You must run `vendor/bin/sail bin pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `vendor/bin/sail bin pint --test --format agent`, simply run `vendor/bin/sail bin pint --format agent` to fix any formatting issues.
|
||||
|
||||
=== pest/core rules ===
|
||||
|
||||
## Pest
|
||||
### Testing
|
||||
- If you need to verify a feature is working, write or update a Unit / Feature test.
|
||||
|
||||
### Pest Tests
|
||||
- All tests must be written using Pest. Use `vendor/bin/sail artisan make:test --pest {name}`.
|
||||
- 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.
|
||||
- Tests should test all of the happy paths, failure paths, and weird paths.
|
||||
- Tests live in the `tests/Feature` and `tests/Unit` directories.
|
||||
- Pest tests look and behave like this:
|
||||
<code-snippet name="Basic Pest Test Example" lang="php">
|
||||
it('is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
</code-snippet>
|
||||
|
||||
### Running Tests
|
||||
- Run the minimal number of tests using an appropriate filter before finalizing code edits.
|
||||
- To run all tests: `vendor/bin/sail artisan test --compact`.
|
||||
- To run all tests in a file: `vendor/bin/sail artisan test --compact tests/Feature/ExampleTest.php`.
|
||||
- To filter on a particular test name: `vendor/bin/sail artisan test --compact --filter=testName` (recommended after making a change to a related file).
|
||||
- When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing.
|
||||
|
||||
### Pest Assertions
|
||||
- When asserting status codes on a response, use the specific method like `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or similar, e.g.:
|
||||
<code-snippet name="Pest Example Asserting postJson Response" lang="php">
|
||||
it('returns all', function () {
|
||||
$response = $this->postJson('/api/docs', []);
|
||||
|
||||
$response->assertSuccessful();
|
||||
});
|
||||
</code-snippet>
|
||||
|
||||
### Mocking
|
||||
- Mocking can be very helpful when appropriate.
|
||||
- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always import it via `use function Pest\Laravel\mock;` before using it. Alternatively, you can use `$this->mock()` if existing tests do.
|
||||
- You can also create partial mocks using the same import or self method.
|
||||
|
||||
### Datasets
|
||||
- Use datasets in Pest to simplify tests that have a lot of duplicated data. This is often the case when testing validation rules, so consider this solution when writing tests for validation rules.
|
||||
|
||||
<code-snippet name="Pest Dataset Example" lang="php">
|
||||
it('has emails', function (string $email) {
|
||||
expect($email)->not->toBeEmpty();
|
||||
})->with([
|
||||
'james' => 'james@laravel.com',
|
||||
'taylor' => 'taylor@laravel.com',
|
||||
]);
|
||||
</code-snippet>
|
||||
</laravel-boost-guidelines>
|
||||
4693
_ide_helper
4693
_ide_helper
File diff suppressed because it is too large
Load diff
36181
_ide_helper.php
36181
_ide_helper.php
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -9,7 +9,7 @@
|
|||
<p class="font-lato weight-300 size-20 nomargin-bottom">
|
||||
Du möchtest Vertriebspartner werden oder hast Fragen zu unseren Produkten?
|
||||
</p>
|
||||
<h3>Jetzt Kontakt aufnehmen: <span><strong style="color:#566d56">+49 (0) 8333 94 61 767</strong></span></h3>
|
||||
<h3>Jetzt Kontakt aufnehmen: <span><strong style="color:#566d56">+49 (0) 8333 946 98 90</strong></span></h3>
|
||||
</div><!-- /left text -->
|
||||
|
||||
|
||||
|
|
@ -41,15 +41,15 @@
|
|||
<address>
|
||||
<ul class="list-unstyled">
|
||||
<li class="footer-sprite address">
|
||||
mivita care GmbH<br>
|
||||
mivita e.K.<br>
|
||||
Leinfeld 2<br>
|
||||
87755 Kirchhaslach<br>
|
||||
</li>
|
||||
<li class="footer-sprite phone">
|
||||
Telefon: +49 (0) 8333 94 61 767
|
||||
Telefon: +49 (0) 8333 946 98 90
|
||||
</li>
|
||||
<li class="footer-sprite email">
|
||||
<a href="mailto:info@mivita.care">info@mivita.care</a>
|
||||
<a href="mailto:info@riwa-tec.de">info@mivita.care</a>
|
||||
</li>
|
||||
</ul>
|
||||
</address>
|
||||
|
|
|
|||
|
|
@ -46,11 +46,12 @@ Zur besseren Verständlichkeit unserer Datenschutzerklärung möchten wir Ihnen
|
|||
<br>
|
||||
Verantwortlicher im Sinne der Datenschutz-Grundverordnung sowie der in den Mitgliedstaaten der Europäischen Union geltenden Datenschutzgesetze und anderer datenschutzrechtlicher Bestimmungen ist:
|
||||
<br><br>
|
||||
<strong>mivita care GmbH</strong><br>
|
||||
<strong>mivita e.K.</strong><br>
|
||||
Herr Alois Ried<br>
|
||||
Leinfeld 2<br>
|
||||
87755 Kirchhaslach<br>
|
||||
Telefon: +49 (0) 8333 94 61 767<br>
|
||||
Telefon: +49 (0) 8333 946 98 90<br>
|
||||
Fax: +49 (0) 8333 7268<br>
|
||||
Mail: info@mivita.care<br>
|
||||
<br><br>
|
||||
<strong>III. Cookies</strong>
|
||||
|
|
|
|||
|
|
@ -8,23 +8,23 @@
|
|||
<h3 class="box-title m-b-0">Impressum</h3>
|
||||
<hr>
|
||||
|
||||
<p><strong>mivita care GmbH</strong><br>
|
||||
<p><strong>mivita e.K.</strong><br>
|
||||
Leinfeld 2<br>
|
||||
87755 Kirchhaslach<br>
|
||||
Telefon: +49 (0) 8333 94 61 767<br>
|
||||
Telefon: +49 (0) 8333 946 98 90<br>
|
||||
Fax: +49 (0) 8333 7268<br>
|
||||
E-Mail: info@mivita.care<br></p>
|
||||
|
||||
<p><strong>Geschäftsführer:</strong><br> Alois Ried<br><br>
|
||||
<p><strong>Geschäftsinhaber:</strong><br> Alois Ried<br><br>
|
||||
|
||||
<strong>Registergericht:</strong><br> Memmingen<br><br>
|
||||
|
||||
<strong>Registernummer:</strong><br> HRB 21591<br><br>
|
||||
<strong>Registernummer:</strong><br> HRA 12236<br><br>
|
||||
|
||||
<strong>USt-ID-Nr.:</strong><br> DE 453867883<br></p>
|
||||
<strong>USt-ID-Nr.:</strong><br> DE 244162340<br></p>
|
||||
<br>
|
||||
<p><strong>Support mivita:</strong><br>
|
||||
Telefon: +49 (0) 8333 94 61 767<br>
|
||||
Telefon: +49 (0) 8333 946 98 90<br>
|
||||
E-Mail:: <a href="mailto:info@mivita.care">info@mivita.care</a></p>
|
||||
|
||||
<br>
|
||||
|
|
|
|||
|
|
@ -141,13 +141,13 @@
|
|||
<hr/>
|
||||
|
||||
<p>
|
||||
<span class="block"><strong><i class="fa fa-map-marker"></i> Adresse:<br></strong> mivita care GmbH<br>
|
||||
<span class="block"><strong><i class="fa fa-map-marker"></i> Adresse:<br></strong> mivita e.K.<br>
|
||||
Leinfeld 2<br>
|
||||
87755 Kirchhaslach</span>
|
||||
<span class="block"><strong><i class="fa fa-phone"></i> Telefon:</strong> <a
|
||||
href="tel:0 8333-94 61 767">0 8333-94 61 767</a></span>
|
||||
href="tel:0 8333-946 98 90">0 8333-946 98 90</a></span>
|
||||
<span class="block"><strong><i class="fa fa-envelope"></i> Email:</strong> <a
|
||||
href="mailto:info@mivita.care">info@mivita.care</a></span>
|
||||
href="mailto:info@riwa-tec.de">info@mivita.care</a></span>
|
||||
</p>
|
||||
|
||||
<hr/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,2 @@
|
|||
TODOS
|
||||
|
||||
- member_id bei Abos aktualisieren, wenn User neu zugewiesen wird oder gelöscht wird
|
||||
-
|
||||
|
||||
|
|
|
|||
|
|
@ -1,169 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\AboChartSnapshot;
|
||||
use App\Models\UserAbo;
|
||||
use App\Services\AboHelper;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class AboStoreChartSnapshots extends Command
|
||||
{
|
||||
protected $signature = 'abo:store-chart-snapshots
|
||||
{--user= : Nur einen bestimmten User berechnen (user_id)}
|
||||
{--force : Bereits vorhandene Snapshots überschreiben}';
|
||||
|
||||
protected $description = 'Speichert monatliche Abo-Zählungen aller vergangenen Monate in der Datenbank (einmalig je Monat)';
|
||||
|
||||
private const SCOPES = ['ot', 'team_abos', 'team_cust_abos'];
|
||||
|
||||
private const START_YEAR = 2026;
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$now = Carbon::now();
|
||||
$force = (bool) $this->option('force');
|
||||
|
||||
// Monate die eingefroren werden sollen: von START_YEAR/01 bis letzten Monat
|
||||
$months = $this->getPastMonths($now);
|
||||
if (empty($months)) {
|
||||
$this->info('Keine vergangenen Monate zum Speichern.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
// User ermitteln
|
||||
$userQuery = User::whereNotNull('m_level')
|
||||
->whereNotNull('payment_account')
|
||||
->where('admin', '<', 4)
|
||||
->whereNull('deleted_at');
|
||||
|
||||
if ($userId = $this->option('user')) {
|
||||
$userQuery->where('id', $userId);
|
||||
}
|
||||
|
||||
$users = $userQuery->select('id')->get();
|
||||
$total = $users->count();
|
||||
$this->info("Berechne Snapshots für {$total} User, ".count($months).' Monate, '.count(self::SCOPES).' Scopes...');
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$inserted = 0;
|
||||
$skipped = 0;
|
||||
|
||||
foreach ($users as $user) {
|
||||
// Bereits vorhandene Snapshots für diesen User laden (zum Überspringen)
|
||||
$existing = AboChartSnapshot::where('user_id', $user->id)
|
||||
->get()
|
||||
->keyBy(fn ($s) => "{$s->scope}_{$s->year}_{$s->month}");
|
||||
|
||||
$teamUserIds = AboHelper::getTeamUserIds($user->id);
|
||||
$rows = [];
|
||||
|
||||
foreach ($months as [$year, $month]) {
|
||||
$startOfMonth = Carbon::create($year, $month, 1)->startOfMonth();
|
||||
$endOfMonth = Carbon::create($year, $month, 1)->endOfMonth();
|
||||
|
||||
foreach (self::SCOPES as $scope) {
|
||||
$key = "{$scope}_{$year}_{$month}";
|
||||
|
||||
if (! $force && $existing->has($key)) {
|
||||
$skipped++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$count = $this->calculateCount($scope, $user->id, $teamUserIds, $startOfMonth, $endOfMonth);
|
||||
$rows[] = [
|
||||
'user_id' => $user->id,
|
||||
'scope' => $scope,
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
'count' => $count,
|
||||
'calculated_at' => $now,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
];
|
||||
$inserted++;
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($rows)) {
|
||||
if ($force) {
|
||||
foreach ($rows as $row) {
|
||||
AboChartSnapshot::updateOrInsert(
|
||||
['user_id' => $row['user_id'], 'scope' => $row['scope'], 'year' => $row['year'], 'month' => $row['month']],
|
||||
$row
|
||||
);
|
||||
}
|
||||
} else {
|
||||
AboChartSnapshot::insertOrIgnore($rows);
|
||||
}
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info("Fertig. Gespeichert: {$inserted}, Übersprungen (bereits vorhanden): {$skipped}");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die Abo-Anzahl für einen Scope/User/Monat anhand der tatsächlichen Daten zum Zeitpunkt der Berechnung.
|
||||
*
|
||||
* @param int[] $teamUserIds
|
||||
*/
|
||||
private function calculateCount(string $scope, int $userId, array $teamUserIds, Carbon $startOfMonth, Carbon $endOfMonth): int
|
||||
{
|
||||
$terminalStatuses = [4, 5];
|
||||
|
||||
$query = match ($scope) {
|
||||
'ot' => UserAbo::where('member_id', $userId)
|
||||
->where('is_for', 'ot')
|
||||
->where('status', '>', 1),
|
||||
'team_abos' => UserAbo::whereIn('user_id', $teamUserIds)
|
||||
->where('is_for', 'me')
|
||||
->where('status', '>', 1),
|
||||
'team_cust_abos' => UserAbo::whereIn('member_id', $teamUserIds)
|
||||
->where('is_for', 'ot')
|
||||
->where('status', '>', 1),
|
||||
};
|
||||
|
||||
return $query
|
||||
->whereDate('start_date', '<=', $endOfMonth)
|
||||
->where(function ($q) use ($startOfMonth, $terminalStatuses) {
|
||||
$q->whereDate('cancel_date', '>=', $startOfMonth)
|
||||
->orWhere(function ($q2) use ($terminalStatuses) {
|
||||
$q2->whereNull('cancel_date')
|
||||
->whereNotIn('status', $terminalStatuses);
|
||||
});
|
||||
})
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle abgeschlossenen Monate von START_YEAR/01 bis letzten Monat.
|
||||
*
|
||||
* @return array<array{int, int}>
|
||||
*/
|
||||
private function getPastMonths(Carbon $now): array
|
||||
{
|
||||
$months = [];
|
||||
$cursor = Carbon::create(self::START_YEAR, 1, 1);
|
||||
$lastMonth = $now->copy()->subMonth()->endOfMonth();
|
||||
|
||||
while ($cursor->lte($lastMonth)) {
|
||||
$months[] = [(int) $cursor->year, (int) $cursor->month];
|
||||
$cursor->addMonth();
|
||||
}
|
||||
|
||||
return $months;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\UserBusinessStructure;
|
||||
use App\Models\UserBusiness;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class BusinessClearData extends Command
|
||||
{
|
||||
/**
|
||||
* php artisan business:clear-data {month} {year}
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'business:clear-data {month} {year} {--force : Force deletion without confirmation}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clear stored business structure data for a specific month/year';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$month = (int) $this->argument('month');
|
||||
$year = (int) $this->argument('year');
|
||||
|
||||
// Validierung
|
||||
if ($month < 1 || $month > 12) {
|
||||
$this->error('Invalid month. Must be between 1 and 12.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$currentYear = (int) date('Y');
|
||||
if ($year < 2020 || $year > $currentYear + 1) {
|
||||
$this->error('Invalid year. Must be between 2020 and ' . ($currentYear + 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Preparing to clear business data for month: {$month} | year: {$year}");
|
||||
|
||||
// Finde bestehende Struktur
|
||||
$existingStructure = UserBusinessStructure::where('year', $year)
|
||||
->where('month', $month)
|
||||
->first();
|
||||
|
||||
if (!$existingStructure) {
|
||||
$this->info('No stored business structure found for the specified month/year');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$structureId = $existingStructure->id;
|
||||
$userBusinessCount = UserBusiness::where('b_structure_id', $structureId)->count();
|
||||
$userCount = is_array($existingStructure->users) ? count($existingStructure->users) : 0;
|
||||
|
||||
$this->info("Found structure ID: {$structureId}");
|
||||
$this->info("- UserBusiness records: {$userBusinessCount}");
|
||||
$this->info("- Users in structure: {$userCount}");
|
||||
$this->info("- Completed: " . ($existingStructure->completed ? 'Yes' : 'No'));
|
||||
|
||||
// Bestätigung (außer bei --force)
|
||||
if (!$this->option('force')) {
|
||||
if (!$this->confirm('Are you sure you want to delete this business structure data?')) {
|
||||
$this->info('Operation cancelled by user');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
// Lösche UserBusiness Einträge
|
||||
if ($userBusinessCount > 0) {
|
||||
$this->info("Deleting {$userBusinessCount} UserBusiness records...");
|
||||
UserBusiness::where('b_structure_id', $structureId)->delete();
|
||||
$this->info('✓ UserBusiness records deleted');
|
||||
}
|
||||
|
||||
// Lösche UserBusinessStructure
|
||||
$this->info('Deleting UserBusinessStructure...');
|
||||
$existingStructure->delete();
|
||||
$this->info('✓ UserBusinessStructure deleted');
|
||||
|
||||
// Garbage Collection
|
||||
gc_collect_cycles();
|
||||
|
||||
$endTime = microtime(true);
|
||||
$duration = round(($endTime - $startTime) * 1000, 2);
|
||||
|
||||
$this->info("✅ Successfully cleared all business data in {$duration}ms");
|
||||
$this->logMemoryUsage();
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error clearing business data: ' . $e->getMessage());
|
||||
$this->error('Stack trace: ' . $e->getTraceAsString());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loggt aktuelle Memory-Nutzung
|
||||
*/
|
||||
private function logMemoryUsage(): void
|
||||
{
|
||||
$currentMemory = memory_get_usage();
|
||||
$peakMemory = memory_get_peak_usage();
|
||||
|
||||
$currentFormatted = $this->formatBytes($currentMemory);
|
||||
$peakFormatted = $this->formatBytes($peakMemory);
|
||||
|
||||
$this->info("Memory - Current: {$currentFormatted} | Peak: {$peakFormatted}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert Bytes in lesbare Einheiten
|
||||
*/
|
||||
private function formatBytes(int $bytes, int $precision = 2): string
|
||||
{
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$i];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\LevelReportService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class BusinessLevelReports extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'business:level-reports {--month= : Filter by specific month} {--year= : Filter by specific year} {--user-id= : Filter by specific user ID} {--csv : Export as CSV file} {--not-updated : Show only users not yet updated to their new level}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate reports showing who achieved new career levels and when';
|
||||
|
||||
private $levelReportService;
|
||||
|
||||
public function __construct(LevelReportService $levelReportService)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->levelReportService = $levelReportService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$this->info('Generiere Level-Aufstieg-Report...');
|
||||
|
||||
// Filter Parameter
|
||||
$filters = [
|
||||
'month' => $this->option('month'),
|
||||
'year' => $this->option('year'),
|
||||
'user_id' => $this->option('user-id'),
|
||||
'only_not_updated' => $this->option('not-updated')
|
||||
];
|
||||
|
||||
$exportCsv = $this->option('csv');
|
||||
|
||||
// Lade Level-Aufstiege über Service
|
||||
$levelPromotions = $this->levelReportService->getLevelPromotions($filters);
|
||||
|
||||
if ($levelPromotions->isEmpty()) {
|
||||
$this->info('Keine Level-Aufstiege gefunden.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($exportCsv) {
|
||||
$filepath = $this->levelReportService->exportToCsv($levelPromotions);
|
||||
$this->info('');
|
||||
$this->info('CSV-Export erstellt: ' . $filepath);
|
||||
$this->info('Anzahl Datensätze: ' . $levelPromotions->count());
|
||||
} else {
|
||||
$this->displayReport($levelPromotions);
|
||||
}
|
||||
|
||||
$this->info('Report erfolgreich generiert.');
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Fehler beim Generieren des Reports: ' . $e->getMessage());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private function displayReport($promotions)
|
||||
{
|
||||
$statistics = $this->levelReportService->getStatistics($promotions);
|
||||
|
||||
$this->info('');
|
||||
$this->info('=== LEVEL-AUFSTIEG REPORT ===');
|
||||
$this->info('');
|
||||
|
||||
if ($promotions->isEmpty()) {
|
||||
$this->info('Keine Level-Aufstiege gefunden.');
|
||||
return;
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'Datum',
|
||||
'User ID',
|
||||
'Name',
|
||||
'E-Mail',
|
||||
'Von Level',
|
||||
'Zu Level',
|
||||
'Aktueller Level',
|
||||
'Margin',
|
||||
'KP Req',
|
||||
'PP Req',
|
||||
'Growth Bonus',
|
||||
'User PP',
|
||||
'User KP',
|
||||
'Level Update',
|
||||
'Aktiv'
|
||||
];
|
||||
|
||||
$rows = [];
|
||||
foreach ($promotions->toArray() as $promotion) {
|
||||
$rows[] = [
|
||||
$promotion['date'],
|
||||
$promotion['user_id'],
|
||||
$promotion['first_name'] . ' ' . $promotion['last_name'],
|
||||
$promotion['email'],
|
||||
$promotion['from_level_name'] . ' (ID:' . $promotion['from_level_id'] . ')',
|
||||
$promotion['to_level_name'] . ' (ID:' . $promotion['to_level_id'] . ')',
|
||||
$promotion['current_user_level_name'] . ' (ID:' . ($promotion['current_user_level_id'] ?? 'N/A') . ')',
|
||||
$promotion['to_level_margin'] . '%',
|
||||
number_format($promotion['to_level_qual_kp'], 0, ',', '.'),
|
||||
number_format($promotion['to_level_qual_pp'], 0, ',', '.'),
|
||||
$promotion['to_level_growth_bonus'] . '%',
|
||||
number_format($promotion['total_pp'], 0, ',', '.'),
|
||||
number_format($promotion['sales_volume_points_sum'], 0, ',', '.'),
|
||||
$promotion['level_updated'],
|
||||
$promotion['active_account'],
|
||||
];
|
||||
}
|
||||
|
||||
$this->table($headers, $rows);
|
||||
|
||||
// Zusammenfassung
|
||||
$this->info('');
|
||||
$this->info('=== ZUSAMMENFASSUNG ===');
|
||||
$this->info('Anzahl Level-Aufstiege: ' . $statistics['total_count']);
|
||||
|
||||
$this->info('');
|
||||
$this->info('Aufstiege nach Ziel-Level:');
|
||||
foreach ($statistics['level_stats'] as $level => $count) {
|
||||
$this->info(" - {$level}: {$count}");
|
||||
}
|
||||
|
||||
$this->info('');
|
||||
$this->info('Aufstiege nach Zeitraum:');
|
||||
foreach ($statistics['period_stats'] as $period => $count) {
|
||||
$this->info(" - {$period}: {$count}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Cron\BusinessUsersStore;
|
||||
use App\Cron\UserLevelUpdate;
|
||||
use App\Cron\UserPaymentCredits;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class BusinessStore extends Command
|
||||
{
|
||||
|
|
@ -17,24 +17,23 @@ class BusinessStore extends Command
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'business:store {month} {year}';
|
||||
protected $signature = 'business:store {month} {year}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create Business Structure and UserDetails with optimized performance';
|
||||
protected $description = 'Create Business Structur and UserDetails';
|
||||
|
||||
private $timeStart;
|
||||
|
||||
private $month;
|
||||
|
||||
private $year;
|
||||
|
||||
private $sendCreditMail = false;
|
||||
private $sendUpdateMail = true;
|
||||
|
||||
|
||||
private $sendUpdateMail = false;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
|
|
@ -46,58 +45,6 @@ class BusinessStore extends Command
|
|||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob der Command heute ausgeführt werden soll
|
||||
*
|
||||
* WICHTIG: Diese Methode verhindert, dass der Command täglich läuft!
|
||||
* Der Command sollte nur am konfigurierten Tag des Monats laufen.
|
||||
*
|
||||
* @return bool True wenn Command ausgeführt werden soll, False sonst
|
||||
*/
|
||||
private function shouldExecuteToday(): bool
|
||||
{
|
||||
// Hole konfigurierten Ausführungstag (Standard: 1 = Monatserster)
|
||||
$executeDay = (int) Setting::getContentBySlug('day-exectute-business-structur');
|
||||
|
||||
// Fallback: Wenn Setting leer oder 0, verwende Tag 1
|
||||
if ($executeDay === 0) {
|
||||
$executeDay = 1;
|
||||
$this->warn('Setting "day-exectute-business-structur" ist leer oder 0. Verwende Standard: Tag 1');
|
||||
\Log::channel('cron')->warning('BusinessStore: Setting day-exectute-business-structur is empty, using default: 1');
|
||||
}
|
||||
|
||||
$presentDay = (int) date('d');
|
||||
|
||||
// Logging für Debugging
|
||||
$this->info("BusinessStore: Configured Day: {$executeDay}, Present Day: {$presentDay}");
|
||||
\Log::channel('cron')->info("BusinessStore: Configured Day: {$executeDay}, Present Day: {$presentDay}");
|
||||
|
||||
// Prüfe ob heute der konfigurierte Tag ist
|
||||
if ($executeDay !== $presentDay) {
|
||||
// Erlaubnis zum Überschreiben für Entwicklung/Testing
|
||||
// ENV-Variable BUSINESS_FORCE_EXECUTE=true überschreibt den Check
|
||||
if (env('BUSINESS_FORCE_EXECUTE', false) === true) {
|
||||
$this->warn('⚠️ BUSINESS_FORCE_EXECUTE ist aktiv - Command wird trotz falschem Tag ausgeführt!');
|
||||
$this->warn('⚠️ Dies sollte NUR auf Test-Servern verwendet werden!');
|
||||
\Log::channel('cron')->warning('BusinessStore: FORCED execution via BUSINESS_FORCE_EXECUTE');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Command sollte heute NICHT laufen
|
||||
$this->info("❌ Command wird NICHT ausgeführt - falscher Tag (erwartet: {$executeDay}, heute: {$presentDay})");
|
||||
\Log::channel('cron')->info("BusinessStore: NOT EXECUTED - wrong day (expected: {$executeDay}, today: {$presentDay})");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Command wird ausgeführt
|
||||
$this->info("✅ Command wird ausgeführt - korrekter Tag ({$presentDay})");
|
||||
\Log::channel('cron')->info("BusinessStore: EXECUTING - correct day ({$presentDay})");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
|
|
@ -105,131 +52,139 @@ class BusinessStore extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
// Prüfe ob Command am richtigen Tag ausgeführt werden soll
|
||||
if (! $this->shouldExecuteToday()) {
|
||||
return 0;
|
||||
}
|
||||
$executeDay = (int) Setting::getContentBySlug('day-exectute-business-structur');
|
||||
$this->info('RUN Command BusinessStore on Day: '. $executeDay);
|
||||
|
||||
$this->logMemoryUsage('Command Start');
|
||||
|
||||
$this->timeStart = microtime(true);
|
||||
|
||||
// Argumente mit Standardwerten für den Vormonat
|
||||
$this->month = $this->argument('month') ?: (int) date('m', strtotime('-1 month'));
|
||||
$this->year = $this->argument('year') ?: (int) date('Y', strtotime('-1 month'));
|
||||
|
||||
$this->info('RUN Command BusinessStore on month: '.$this->month.' | year: '.$this->year);
|
||||
\Log::channel('cron')->info('RUN Command BusinessStore on month: '.$this->month.' | year: '.$this->year);
|
||||
$this->logMemoryUsage('Parameters initialized');
|
||||
|
||||
// Prozesse ausführen mit Fehlerbehandlung
|
||||
$this->executeWithErrorHandling('Business Structure Storage', function () {
|
||||
$this->storeBusinessStructureUsersDetailMonth();
|
||||
});
|
||||
|
||||
$this->executeWithErrorHandling('Commission Calculation', function () {
|
||||
$this->userBusinessCommissionsToCredit();
|
||||
});
|
||||
|
||||
// Auskommentierte Prozesse bleiben inaktiv
|
||||
// $this->userCreatePaymentCreditsPDF();
|
||||
// $this->userLevelUpdate();
|
||||
// $this->storeBusinessStructureUsersDetailPeriod(1, 6);
|
||||
|
||||
$this->logExecutionTime('COMMAND COMPLETED SUCCESSFULLY');
|
||||
$this->logMemoryUsage('Command End');
|
||||
\Log::channel('cron')->info('COMMAND COMPLETED SUCCESSFULLY');
|
||||
$presentDay = (int) date('d');
|
||||
$this->info('RUN Command BusinessStore present Day: '. $presentDay);
|
||||
|
||||
if($executeDay !== $presentDay){
|
||||
$this->info('NOT RUN Command BusinessStore is not present Day: '. $presentDay);
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Command failed with error: '.$e->getMessage());
|
||||
$this->error('Stack trace: '.$e->getTraceAsString());
|
||||
$this->logExecutionTime('COMMAND FAILED');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->timeStart = microtime(true);
|
||||
//$this->info('RUN Command BusinessStore');
|
||||
$this->month = $this->argument('month');
|
||||
$this->year = $this->argument('year');
|
||||
|
||||
|
||||
if(!$this->month || !$this->year){
|
||||
$this->month = (int) date("m", strtotime("-1 month", time()));
|
||||
$this->year = (int) date("Y", strtotime("-1 month", time()));
|
||||
}
|
||||
|
||||
$this->info('RUN Command BusinessStore on month: '.$this->month.' | year: '.$this->year);
|
||||
|
||||
//erstellt die Business Struktur und die Details
|
||||
$this->storeBusinessStructureUsersDetailMonth();
|
||||
|
||||
//Struktur und die Details für mehrere Montate for / to month
|
||||
//$this->storeBusinessStructureUsersDetailPeriod(1, 6);
|
||||
|
||||
//erstellt die Gutschriften
|
||||
//$this->userBusinessCommissionsToCredit();
|
||||
|
||||
//erstellt aus den Gutschriften die PDFs
|
||||
//$this->userCreatePaymentCreditsPDF();
|
||||
|
||||
//update user Level
|
||||
//$this->userLevelUpdate();
|
||||
return 0;
|
||||
|
||||
//\Log::info('Cron is running');
|
||||
//return 0;
|
||||
}
|
||||
|
||||
private function storeBusinessStructureUsersDetailMonth()
|
||||
{
|
||||
private function storeBusinessStructureUsersDetailMonth(){
|
||||
|
||||
$this->info('storeBusinessStructureUsersDetailMonth month: '.$this->month.' year:'.$this->year);
|
||||
$businessUsersStore = new BusinessUsersStore($this->month, $this->year);
|
||||
$businessUsersStore->storeUserBusinessStructure();
|
||||
$businessUsersStore->storeBusinessUsersDetail();
|
||||
$bool = $businessUsersStore->storeBusinessCompleted();
|
||||
|
||||
$this->logExecutionTime('END Command storeBusinessStructureUsersDetailMonth: '.$bool);
|
||||
$businessUsersStore = new BusinessUsersStore($this->month, $this->year);
|
||||
$businessUsersStore->storeUserBusinessStructure();
|
||||
$businessUsersStore->storeBusinessUsersDetail();
|
||||
$bool = $businessUsersStore->storeBusinessCompleted();
|
||||
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info('END Command storeBusinessStructureUsersDetailMonth: '.$bool. ' | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
|
||||
}
|
||||
|
||||
private function userBusinessCommissionsToCredit()
|
||||
{
|
||||
private function userBusinessCommissionsToCredit(){
|
||||
|
||||
$this->info('userBusinessCommissionsToCredit month: '.$this->month.' year:'.$this->year);
|
||||
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
|
||||
$userBusinesses = $userPaymentCredits->getUserBusinessByMonthYear();
|
||||
|
||||
foreach ($userBusinesses as $userBusiness) {
|
||||
foreach($userBusinesses as $userBusiness){
|
||||
$ret = $userPaymentCredits->addUserCreditItem($userBusiness);
|
||||
$this->info('userBusinessCredit: '.$ret->user_id.' : Team: '.$ret->commission_pp_total.' | Shop: '.$ret->commission_shop_sales);
|
||||
$this->info('userBusinessCredit: '.$ret->user_id.' : Team: '.$ret->commission_team_total.' | Shop: '.$ret->commission_shop_sales);
|
||||
}
|
||||
$this->logExecutionTime('END Command userBusinessCommissionsToCredit:');
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info('END Command userBusinessCommissionsToCredit: | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
|
||||
}
|
||||
|
||||
private function userCreatePaymentCreditsPDF()
|
||||
{
|
||||
private function userCreatePaymentCreditsPDF(){
|
||||
|
||||
$this->info('userCreatePaymentCreditsPDF month: '.$this->month.' year:'.$this->year);
|
||||
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
|
||||
$creditItemUsers = $userPaymentCredits->getUserCreditItemUsersByMonthYear();
|
||||
|
||||
foreach ($creditItemUsers as $creditItemUser) {
|
||||
foreach($creditItemUsers as $creditItemUser){
|
||||
$bool = $userPaymentCredits->makeCreditPaymentPDF($creditItemUser->user_id, $this->sendCreditMail);
|
||||
$this->info('creditsPDF: '.$bool.' user_id: '.$creditItemUser->user_id);
|
||||
}
|
||||
|
||||
$this->logExecutionTime('END Command userCreatePaymentCreditsPDF:');
|
||||
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info('END Command userCreatePaymentCreditsPDF: | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
|
||||
}
|
||||
|
||||
private function userLevelUpdate()
|
||||
{
|
||||
private function userLevelUpdate(){
|
||||
|
||||
$this->info('userLevelUpdate month: '.$this->month.' year:'.$this->year);
|
||||
|
||||
$userLevelUpdate = new UserLevelUpdate($this->month, $this->year);
|
||||
$levelUpdateUsers = $userLevelUpdate->getUserBusinessByMonthYear();
|
||||
|
||||
foreach ($levelUpdateUsers as $userBusiness) {
|
||||
|
||||
foreach($levelUpdateUsers as $userBusiness){
|
||||
$ret = $userLevelUpdate->makeUserLevelUpdate($userBusiness, $this->sendUpdateMail);
|
||||
if ($ret) {
|
||||
if($ret){
|
||||
$this->info('updateLevel: '.$userBusiness->user->id.' | '.$userBusiness->user->email.' | '.
|
||||
'from: '.$userBusiness->m_level_id.' '.$userBusiness->user_level_name.' | '.
|
||||
'to: '.$ret);
|
||||
'from: '.$userBusiness->m_level_id.' '.$userBusiness->user_level_name.' | '.
|
||||
'to: '.$ret);
|
||||
}
|
||||
|
||||
}
|
||||
$this->logExecutionTime('END Command userLevelUpdate:');
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info('END Command userLevelUpdate: | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
|
||||
}
|
||||
|
||||
private function storeBusinessStructureUsersDetailPeriod($from, $to)
|
||||
{
|
||||
for ($i = $from; $i <= $to; $i++) {
|
||||
$this->info('Store Business Structure Users Detail month: '.$i.' year:'.$this->year);
|
||||
$businessUsersStore = new BusinessUsersStore($i, $this->year);
|
||||
|
||||
private function storeBusinessStructureUsersDetailPeriod($for, $to){
|
||||
for($i = $for; $i<=$to; $i++){
|
||||
$month = $i;
|
||||
$this->info('Store Business Structure Users Detail month: '.$month.' year:'.$this->year);
|
||||
$businessUsersStore = new BusinessUsersStore($month, $this->year);
|
||||
$businessUsersStore->storeUserBusinessStructure();
|
||||
$businessUsersStore->storeBusinessUsersDetail();
|
||||
$bool = $businessUsersStore->storeBusinessCompleted();
|
||||
|
||||
$this->logExecutionTime('Period BusinessStore: '.$bool);
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
$this->info('Period BusinessStore: '.$bool. ' | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function logExecutionTime($message)
|
||||
{
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info($message.' | Time: '.$sec.'sec :'.round($micro * 1000, 4).' ms');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,526 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Cron\BusinessUsersStoreOptimized;
|
||||
use App\Cron\UserLevelUpdate;
|
||||
use App\Cron\UserPaymentCredits;
|
||||
use App\Models\Setting;
|
||||
use App\Models\UserBusiness;
|
||||
use App\Models\UserBusinessStructure;
|
||||
use App\Models\UserSalesVolume;
|
||||
use App\Services\BusinessPlan\SalesPointsVolume;
|
||||
use App\User;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class BusinessStoreOptimized extends Command
|
||||
{
|
||||
/**
|
||||
* ln -sfv /usr/bin/php73 /usr/bin/php
|
||||
* php artisan business:store-optimized month year
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'business:store-optimized {month} {year} {--clear : Clear stored data before processing}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create Business Structure and UserDetails with optimized performance and monitoring';
|
||||
|
||||
private $timeStart;
|
||||
|
||||
private $month;
|
||||
|
||||
private $year;
|
||||
|
||||
private $sendCreditMail = false;
|
||||
|
||||
private $sendUpdateMail = false;
|
||||
|
||||
/**
|
||||
* Getter für sendUpdateMail (für Tests)
|
||||
*/
|
||||
public function getSendUpdateMail(): bool
|
||||
{
|
||||
return $this->sendUpdateMail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter für sendUpdateMail (für Tests)
|
||||
*/
|
||||
public function setSendUpdateMail(bool $sendUpdateMail): void
|
||||
{
|
||||
$this->sendUpdateMail = $sendUpdateMail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob der Command heute ausgeführt werden soll
|
||||
*
|
||||
* WICHTIG: Diese Methode verhindert, dass der Command täglich läuft!
|
||||
* Der Command sollte nur am konfigurierten Tag des Monats laufen.
|
||||
*
|
||||
* @return bool True wenn Command ausgeführt werden soll, False sonst
|
||||
*/
|
||||
private function shouldExecuteToday(): bool
|
||||
{
|
||||
// Hole konfigurierten Ausführungstag (Standard: 1 = Monatserster)
|
||||
$executeDay = (int) Setting::getContentBySlug('day-exectute-business-structur');
|
||||
|
||||
// Fallback: Wenn Setting leer oder 0, verwende Tag 1
|
||||
if ($executeDay === 0) {
|
||||
$executeDay = 1;
|
||||
$this->warn('Setting "day-exectute-business-structur" ist leer oder 0. Verwende Standard: Tag 1');
|
||||
\Log::channel('cron')->warning('BusinessStoreOptimized: Setting day-exectute-business-structur is empty, using default: 1');
|
||||
}
|
||||
|
||||
$presentDay = (int) date('d');
|
||||
|
||||
// Logging für Debugging
|
||||
$this->info("BusinessStoreOptimized: Configured Day: {$executeDay}, Present Day: {$presentDay}");
|
||||
\Log::channel('cron')->info("BusinessStoreOptimized: Configured Day: {$executeDay}, Present Day: {$presentDay}");
|
||||
|
||||
// Prüfe ob heute der konfigurierte Tag ist
|
||||
if ($executeDay !== $presentDay) {
|
||||
// Erlaubnis zum Überschreiben für Entwicklung/Testing
|
||||
// ENV-Variable BUSINESS_FORCE_EXECUTE=true überschreibt den Check
|
||||
if (env('BUSINESS_FORCE_EXECUTE', false) === true) {
|
||||
$this->warn('⚠️ BUSINESS_FORCE_EXECUTE ist aktiv - Command wird trotz falschem Tag ausgeführt!');
|
||||
$this->warn('⚠️ Dies sollte NUR auf Test-Servern verwendet werden!');
|
||||
\Log::channel('cron')->warning('BusinessStoreOptimized: FORCED execution via BUSINESS_FORCE_EXECUTE');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Command sollte heute NICHT laufen
|
||||
$this->info("❌ Command wird NICHT ausgeführt - falscher Tag (erwartet: {$executeDay}, heute: {$presentDay})");
|
||||
\Log::channel('cron')->info("BusinessStoreOptimized: NOT EXECUTED - wrong day (expected: {$executeDay}, today: {$presentDay})");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Command wird ausgeführt
|
||||
$this->info("✅ Command wird ausgeführt - korrekter Tag ({$presentDay})");
|
||||
\Log::channel('cron')->info("BusinessStoreOptimized: EXECUTING - correct day ({$presentDay})");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
// Prüfe ob Command am richtigen Tag ausgeführt werden soll
|
||||
if (! $this->shouldExecuteToday()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->logMemoryUsage('Command Start');
|
||||
|
||||
$this->timeStart = microtime(true);
|
||||
|
||||
// Argumente mit Standardwerten für den Vormonat
|
||||
$this->month = $this->argument('month') ?: (int) date('m', strtotime('-1 month'));
|
||||
$this->year = $this->argument('year') ?: (int) date('Y', strtotime('-1 month'));
|
||||
|
||||
$this->info('RUN Command BusinessStoreOptimized on month: '.$this->month.' | year: '.$this->year);
|
||||
$this->logMemoryUsage('Parameters initialized');
|
||||
|
||||
// Prüfe --clear Option und lösche gespeicherte Daten falls gewünscht
|
||||
if ($this->option('clear')) {
|
||||
$this->executeWithErrorHandling('Clear Stored Data', function () {
|
||||
$this->clearStoredData();
|
||||
});
|
||||
}
|
||||
|
||||
// Prozesse ausführen mit optimierter Fehlerbehandlung
|
||||
$this->executeWithErrorHandling('Business Structure Storage', function () {
|
||||
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized Business Structure Storage');
|
||||
$this->storeBusinessStructureUsersDetailMonth();
|
||||
});
|
||||
|
||||
$this->executeWithErrorHandling('Commission Calculation', function () {
|
||||
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized Commission Calculation');
|
||||
$this->userBusinessCommissionsToCredit();
|
||||
});
|
||||
|
||||
$this->executeWithErrorHandling('User Level Update', function () {
|
||||
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized User Level Update');
|
||||
$this->userLevelUpdate();
|
||||
});
|
||||
|
||||
$this->executeWithErrorHandling('Monthly Qual-KP Bonus Points', function () {
|
||||
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized Monthly Qual-KP Bonus Points');
|
||||
$this->assignMonthlyQualKpBonusPoints();
|
||||
});
|
||||
// Auskommentierte Prozesse bleiben inaktiv
|
||||
// $this->userCreatePaymentCreditsPDF();
|
||||
// $this->storeBusinessStructureUsersDetailPeriod(1, 6);
|
||||
|
||||
$this->logExecutionTime('COMMAND COMPLETED SUCCESSFULLY');
|
||||
$this->logMemoryUsage('Command End');
|
||||
\Log::channel('cron')->info('COMMAND COMPLETED SUCCESSFULLY');
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Command failed with error: '.$e->getMessage());
|
||||
$this->error('Stack trace: '.$e->getTraceAsString());
|
||||
$this->logExecutionTime('COMMAND FAILED');
|
||||
\Log::channel('cron')->info('COMMAND FAILED');
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private function storeBusinessStructureUsersDetailMonth()
|
||||
{
|
||||
$this->info('storeBusinessStructureUsersDetailMonth month: '.$this->month.' year:'.$this->year);
|
||||
|
||||
try {
|
||||
$businessUsersStore = new BusinessUsersStoreOptimized($this->month, $this->year);
|
||||
$businessUsersStore->storeUserBusinessStructure();
|
||||
$businessUsersStore->storeBusinessUsersDetail();
|
||||
$bool = $businessUsersStore->storeBusinessCompleted();
|
||||
|
||||
$this->logExecutionTime('END Command storeBusinessStructureUsersDetailMonth: '.$bool);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error in storeBusinessStructureUsersDetailMonth: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function userBusinessCommissionsToCredit()
|
||||
{
|
||||
$this->info('userBusinessCommissionsToCredit month: '.$this->month.' year:'.$this->year);
|
||||
|
||||
try {
|
||||
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
|
||||
$userBusinesses = $userPaymentCredits->getUserBusinessByMonthYear();
|
||||
|
||||
$processedCount = 0;
|
||||
foreach ($userBusinesses as $userBusiness) {
|
||||
$ret = $userPaymentCredits->addUserCreditItem($userBusiness);
|
||||
$this->info('userBusinessCredit: '.$ret->user_id.' : Team: '.$ret->commission_pp_total.' | Shop: '.$ret->commission_shop_sales);
|
||||
$processedCount++;
|
||||
|
||||
// Memory-Check alle 100 User
|
||||
if ($processedCount % 100 === 0) {
|
||||
$this->logMemoryUsage("After processing {$processedCount} users");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("Processed {$processedCount} user businesses total");
|
||||
$this->logExecutionTime('END Command userBusinessCommissionsToCredit:');
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error in userBusinessCommissionsToCredit: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function userCreatePaymentCreditsPDF()
|
||||
{
|
||||
$this->info('userCreatePaymentCreditsPDF month: '.$this->month.' year:'.$this->year);
|
||||
|
||||
try {
|
||||
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
|
||||
$creditItemUsers = $userPaymentCredits->getUserCreditItemUsersByMonthYear();
|
||||
|
||||
$processedCount = 0;
|
||||
foreach ($creditItemUsers as $creditItemUser) {
|
||||
$bool = $userPaymentCredits->makeCreditPaymentPDF($creditItemUser->user_id, $this->sendCreditMail);
|
||||
$this->info('creditsPDF: '.$bool.' user_id: '.$creditItemUser->user_id);
|
||||
$processedCount++;
|
||||
|
||||
// Memory-Check alle 50 PDFs
|
||||
if ($processedCount % 50 === 0) {
|
||||
$this->logMemoryUsage("After processing {$processedCount} PDFs");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("Created {$processedCount} PDF files total");
|
||||
$this->logExecutionTime('END Command userCreatePaymentCreditsPDF:');
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error in userCreatePaymentCreditsPDF: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert User-Level basierend auf next_qual_user_level
|
||||
* Kann auch von Tests aufgerufen werden
|
||||
*/
|
||||
public function userLevelUpdate()
|
||||
{
|
||||
$this->info('userLevelUpdate month: '.$this->month.' year:'.$this->year);
|
||||
|
||||
try {
|
||||
$userLevelUpdate = new UserLevelUpdate($this->month, $this->year);
|
||||
$levelUpdateUsers = $userLevelUpdate->getUserBusinessByMonthYear();
|
||||
|
||||
$this->info('Found '.$levelUpdateUsers->count().' user businesses with level promotions to process');
|
||||
|
||||
$updatedCount = 0;
|
||||
$skippedCount = 0;
|
||||
$errorCount = 0;
|
||||
|
||||
foreach ($levelUpdateUsers as $userBusiness) {
|
||||
try {
|
||||
$ret = $userLevelUpdate->makeUserLevelUpdate($userBusiness, $this->sendUpdateMail);
|
||||
if ($ret) {
|
||||
$oldLevel = $userBusiness->m_level_id.' '.($userBusiness->user_level_name ?? 'N/A');
|
||||
$this->info('updateLevel: User '.$userBusiness->user->id.
|
||||
' | '.$userBusiness->user->email.
|
||||
' | from: '.$oldLevel.
|
||||
' | to: '.$ret);
|
||||
$updatedCount++;
|
||||
} else {
|
||||
$skippedCount++;
|
||||
}
|
||||
|
||||
// Memory-Check alle 50 User
|
||||
if (($updatedCount + $skippedCount) % 50 === 0) {
|
||||
$this->logMemoryUsage('After processing '.($updatedCount + $skippedCount).' users');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$errorCount++;
|
||||
$this->warn('Error updating level for UserBusiness '.$userBusiness->id.': '.$e->getMessage());
|
||||
\Log::channel('cron')->warning('UserLevelUpdate error for UserBusiness '.$userBusiness->id.': '.$e->getMessage());
|
||||
|
||||
// Weiter mit nächstem User statt abzubrechen
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("Level update completed: {$updatedCount} updated, {$skippedCount} skipped, {$errorCount} errors");
|
||||
$this->logExecutionTime('END Command userLevelUpdate:');
|
||||
$this->logMemoryUsage('After userLevelUpdate');
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error in userLevelUpdate: '.$e->getMessage());
|
||||
$this->error('Stack trace: '.$e->getTraceAsString());
|
||||
\Log::channel('cron')->error('UserLevelUpdate command failed: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function storeBusinessStructureUsersDetailPeriod($from, $to)
|
||||
{
|
||||
try {
|
||||
for ($i = $from; $i <= $to; $i++) {
|
||||
$this->info('Store Business Structure Users Detail month: '.$i.' year:'.$this->year);
|
||||
$this->logMemoryUsage("Before month {$i}");
|
||||
|
||||
$businessUsersStore = new BusinessUsersStoreOptimized($i, $this->year);
|
||||
$businessUsersStore->storeUserBusinessStructure();
|
||||
$businessUsersStore->storeBusinessUsersDetail();
|
||||
$bool = $businessUsersStore->storeBusinessCompleted();
|
||||
|
||||
$this->logExecutionTime('Period BusinessStore: '.$bool);
|
||||
$this->logMemoryUsage("After month {$i}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error in storeBusinessStructureUsersDetailPeriod: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht gespeicherte Business Structure Daten für den angegebenen Monat/Jahr
|
||||
*/
|
||||
private function clearStoredData()
|
||||
{
|
||||
try {
|
||||
$this->info("Clearing stored business data for month: {$this->month} | year: {$this->year}");
|
||||
|
||||
// Finde bestehende UserBusinessStructure
|
||||
$existingStructure = UserBusinessStructure::where('year', $this->year)
|
||||
->where('month', $this->month)
|
||||
->first();
|
||||
|
||||
if (! $existingStructure) {
|
||||
$this->info('No stored business structure found to clear');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$structureId = $existingStructure->id;
|
||||
$this->info("Found existing structure with ID: {$structureId}");
|
||||
|
||||
// Lösche zugehörige UserBusiness Einträge
|
||||
$deletedUserBusinesses = UserBusiness::where('b_structure_id', $structureId)->count();
|
||||
if ($deletedUserBusinesses > 0) {
|
||||
UserBusiness::where('b_structure_id', $structureId)->delete();
|
||||
$this->info("Deleted {$deletedUserBusinesses} UserBusiness records");
|
||||
}
|
||||
|
||||
// Lösche die UserBusinessStructure
|
||||
$existingStructure->delete();
|
||||
$this->info("Deleted UserBusinessStructure with ID: {$structureId}");
|
||||
|
||||
// Garbage Collection nach dem Löschen
|
||||
gc_collect_cycles();
|
||||
|
||||
$this->info('Successfully cleared all stored business data');
|
||||
$this->logMemoryUsage('After clearing data');
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error clearing stored data: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schreibt ausgewählten Usern einmal pro Monat ihre Level-qual_kp als KP-Bonus gut.
|
||||
* Idempotent: Ein bereits vorhandener Eintrag für diesen Monat/Jahr wird übersprungen.
|
||||
*
|
||||
* @var array<int> User-IDs die den monatlichen Qual-KP-Bonus erhalten sollen
|
||||
*/
|
||||
private function assignMonthlyQualKpBonusPoints(): void
|
||||
{
|
||||
$bonusUserIds = [486];
|
||||
|
||||
$month = date('m');
|
||||
$year = date('Y');
|
||||
|
||||
$users = User::query()
|
||||
->whereIn('id', $bonusUserIds)
|
||||
->whereNotNull('m_level')
|
||||
->with('user_level')
|
||||
->get()
|
||||
->filter(fn (User $user) => $user->user_level && $user->user_level->qual_kp > 0);
|
||||
|
||||
$assigned = 0;
|
||||
$skipped = 0;
|
||||
|
||||
foreach ($users as $user) {
|
||||
$alreadyExists = UserSalesVolume::where('user_id', $user->id)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->where('info', 'qual_kp_bonus')
|
||||
->exists();
|
||||
|
||||
if ($alreadyExists) {
|
||||
$skipped++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
SalesPointsVolume::addSalesPointsVolume([
|
||||
'user_id' => $user->id,
|
||||
'points' => $user->user_level->qual_kp,
|
||||
'status_points' => 2,
|
||||
'total_net' => 0,
|
||||
'status_turnover' => 1,
|
||||
'info' => 'qual_kp_bonus',
|
||||
]);
|
||||
|
||||
$assigned++;
|
||||
}
|
||||
|
||||
$this->info("Qual-KP Bonus: {$assigned} zugewiesen, {$skipped} übersprungen (bereits vorhanden)");
|
||||
\Log::channel('cron')->info("Qual-KP Bonus Points: assigned={$assigned}, skipped={$skipped}");
|
||||
}
|
||||
|
||||
private function logExecutionTime($message)
|
||||
{
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info($message.' | Time: '.$sec.'sec :'.round($micro * 1000, 4).' ms');
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt eine Funktion mit Fehlerbehandlung aus
|
||||
*/
|
||||
private function executeWithErrorHandling(string $processName, callable $callback): void
|
||||
{
|
||||
try {
|
||||
$startTime = microtime(true);
|
||||
$this->info("Starting: {$processName}");
|
||||
$this->logMemoryUsage("Before {$processName}");
|
||||
|
||||
$callback();
|
||||
|
||||
$endTime = microtime(true);
|
||||
$duration = round(($endTime - $startTime) * 1000, 2);
|
||||
$this->info("Completed: {$processName} in {$duration}ms");
|
||||
$this->logMemoryUsage("After {$processName}");
|
||||
} catch (\Exception $e) {
|
||||
$this->error("Error in {$processName}: ".$e->getMessage());
|
||||
$this->error('Stack trace: '.$e->getTraceAsString());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loggt aktuelle Memory-Nutzung
|
||||
*/
|
||||
private function logMemoryUsage(string $checkpoint): void
|
||||
{
|
||||
$currentMemory = memory_get_usage();
|
||||
$peakMemory = memory_get_peak_usage();
|
||||
$memoryLimit = $this->parseMemoryLimit(ini_get('memory_limit'));
|
||||
|
||||
$currentFormatted = $this->formatBytes($currentMemory);
|
||||
$peakFormatted = $this->formatBytes($peakMemory);
|
||||
$limitFormatted = $this->formatBytes($memoryLimit);
|
||||
$usagePercent = round(($currentMemory / $memoryLimit) * 100, 2);
|
||||
|
||||
$this->info("[{$checkpoint}] Memory: {$currentFormatted} / {$limitFormatted} ({$usagePercent}%) | Peak: {$peakFormatted}");
|
||||
|
||||
if ($usagePercent > 80) {
|
||||
$this->warn("High memory usage detected at {$checkpoint}: {$usagePercent}%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert Memory-Limit String zu Bytes
|
||||
*/
|
||||
private function parseMemoryLimit(string $limit): int
|
||||
{
|
||||
$limit = trim($limit);
|
||||
$last = strtolower($limit[strlen($limit) - 1]);
|
||||
$number = (int) $limit;
|
||||
|
||||
switch ($last) {
|
||||
case 'g':
|
||||
$number *= 1024;
|
||||
case 'm':
|
||||
$number *= 1024;
|
||||
case 'k':
|
||||
$number *= 1024;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert Bytes in lesbare Einheiten
|
||||
*/
|
||||
private function formatBytes(int $bytes, int $precision = 2): string
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
||||
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
return round($bytes, $precision).' '.$units[$i];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Cron\UserPaymentCredits;
|
||||
use App\Models\UserBusiness;
|
||||
use App\Services\BusinessPlan\BusinessUserItemOptimized;
|
||||
use App\Services\BusinessPlan\TreeCalcBotOptimized;
|
||||
use App\User;
|
||||
use Illuminate\Console\Command;
|
||||
use stdClass;
|
||||
|
||||
class BusinessTestAccount extends Command
|
||||
{
|
||||
/**
|
||||
* php artisan business:test-account {user_id} {month} {year}
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'business:test-account {user_id} {month} {year} {--commissions : Calculate and show user commissions}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Test account data loading for a specific user in business calculations, with optional commission calculation';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$userId = (int) $this->argument('user_id');
|
||||
$month = (int) $this->argument('month');
|
||||
$year = (int) $this->argument('year');
|
||||
|
||||
$this->info("Testing account data for User ID: {$userId}, Month: {$month}, Year: {$year}");
|
||||
$this->line('');
|
||||
|
||||
// Lade User mit Account
|
||||
$user = User::with('account', 'user_level')->find($userId);
|
||||
|
||||
if (!$user) {
|
||||
$this->error("User {$userId} not found");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("User found: {$user->email}");
|
||||
$this->info("Account ID: " . ($user->account_id ?? 'NULL'));
|
||||
|
||||
if ($user->account) {
|
||||
$this->info("Account loaded: YES");
|
||||
$this->info("Account m_account: " . ($user->account->m_account ?? 'NULL'));
|
||||
$this->info("Account first_name: " . ($user->account->first_name ?? 'NULL'));
|
||||
$this->info("Account last_name: " . ($user->account->last_name ?? 'NULL'));
|
||||
$this->info("Account birthday: " . ($user->account->birthday ?? 'NULL'));
|
||||
$this->info("Account phone: " . ($user->account->getPhoneNumber() ?? 'NULL'));
|
||||
} else {
|
||||
$this->warn("Account loaded: NO");
|
||||
}
|
||||
|
||||
$this->line('');
|
||||
$this->info('Testing BusinessUserItemOptimized...');
|
||||
|
||||
// Erstelle Date Object
|
||||
$date = new stdClass();
|
||||
$date->month = $month;
|
||||
$date->year = $year;
|
||||
$date->start_date = "{$year}-{$month}-01 00:00:00";
|
||||
$date->end_date = date('Y-m-t 23:59:59', strtotime("{$year}-{$month}-01"));
|
||||
|
||||
// Teste BusinessUserItemOptimized
|
||||
$TreeCalcBot = new TreeCalcBotOptimized($date->month, $date->year, 'admin');
|
||||
$TreeCalcBot->initBusinesslUserDetail($user, true);
|
||||
|
||||
$businessUserItem = $TreeCalcBot->getItem();
|
||||
|
||||
$bUser = $businessUserItem->getBUser();
|
||||
|
||||
$this->line('');
|
||||
$this->info('Results from BusinessUserItemOptimized:');
|
||||
$this->info("m_account: " . ($bUser->m_account ?? 'NULL'));
|
||||
$this->info("first_name: " . ($bUser->first_name ?? 'NULL'));
|
||||
$this->info("last_name: " . ($bUser->last_name ?? 'NULL'));
|
||||
$this->info("user_birthday: " . ($bUser->user_birthday ?? 'NULL'));
|
||||
$this->info("user_phone: " . ($bUser->user_phone ?? 'NULL'));
|
||||
$this->info("email: " . ($bUser->email ?? 'NULL'));
|
||||
|
||||
$this->line('');
|
||||
$this->info('Sales Volume Fields:');
|
||||
$this->info("sales_volume_KP_points: " . ($bUser->sales_volume_KP_points ?? 'NULL'));
|
||||
$this->info("sales_volume_TP_points: " . ($bUser->sales_volume_TP_points ?? 'NULL'));
|
||||
$this->info("sales_volume_points_shop: " . ($bUser->sales_volume_points_shop ?? 'NULL'));
|
||||
$this->info("sales_volume_points_KP_sum: " . ($bUser->sales_volume_points_KP_sum ?? 'NULL'));
|
||||
$this->info("sales_volume_points_TP_sum: " . ($bUser->sales_volume_points_TP_sum ?? 'NULL'));
|
||||
$this->info("sales_volume_total: " . ($bUser->sales_volume_total ?? 'NULL'));
|
||||
$this->info("sales_volume_total_shop: " . ($bUser->sales_volume_total_shop ?? 'NULL'));
|
||||
$this->info("sales_volume_total_sum: " . ($bUser->sales_volume_total_sum ?? 'NULL'));
|
||||
|
||||
$this->line('');
|
||||
$this->info('Commission Fields:');
|
||||
$this->info("payline_points: " . ($bUser->payline_points ?? 'NULL'));
|
||||
$this->info("commission_pp_total: " . ($bUser->commission_pp_total ?? 'NULL'));
|
||||
$this->info("commission_shop_sales: " . ($bUser->commission_shop_sales ?? 'NULL'));
|
||||
$this->info("commission_growth_total: " . ($bUser->commission_growth_total ?? 'NULL'));
|
||||
|
||||
// Test UserSalesVolume directly
|
||||
$this->line('');
|
||||
$this->info('Testing UserSalesVolume data directly:');
|
||||
$userSalesVolume = $user->getUserSalesVolume($month, $year, 'first');
|
||||
if ($userSalesVolume) {
|
||||
$this->info("UserSalesVolume found: ID {$userSalesVolume->id}");
|
||||
$this->info("month_KP_points: " . ($userSalesVolume->month_KP_points ?? 'NULL'));
|
||||
$this->info("month_TP_points: " . ($userSalesVolume->month_TP_points ?? 'NULL'));
|
||||
$this->info("month_shop_points: " . ($userSalesVolume->month_shop_points ?? 'NULL'));
|
||||
$this->info("month_total_net: " . ($userSalesVolume->month_total_net ?? 'NULL'));
|
||||
$this->info("month_shop_total_net: " . ($userSalesVolume->month_shop_total_net ?? 'NULL'));
|
||||
} else {
|
||||
$this->warn("No UserSalesVolume found for month {$month}/{$year}");
|
||||
|
||||
// Check if any UserSalesVolume exists for this user
|
||||
$anyVolume = \App\Models\UserSalesVolume::where('user_id', $userId)->orderBy('year', 'desc')->orderBy('month', 'desc')->first();
|
||||
if ($anyVolume) {
|
||||
$this->info("Latest UserSalesVolume found: {$anyVolume->month}/{$anyVolume->year}");
|
||||
} else {
|
||||
$this->warn("No UserSalesVolume records found for this user at all");
|
||||
}
|
||||
}
|
||||
|
||||
$this->line('');
|
||||
|
||||
// Prüfe ob UserBusiness bereits gespeichert ist
|
||||
$existingUserBusiness = UserBusiness::where('user_id', $userId)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->first();
|
||||
|
||||
if ($existingUserBusiness) {
|
||||
$this->info('Existing UserBusiness found:');
|
||||
$this->info("m_account: " . ($existingUserBusiness->m_account ?? 'NULL'));
|
||||
$this->info("first_name: " . ($existingUserBusiness->first_name ?? 'NULL'));
|
||||
$this->info("last_name: " . ($existingUserBusiness->last_name ?? 'NULL'));
|
||||
$this->info("user_birthday: " . ($existingUserBusiness->user_birthday ?? 'NULL'));
|
||||
$this->info("user_phone: " . ($existingUserBusiness->user_phone ?? 'NULL'));
|
||||
$this->info("email: " . ($existingUserBusiness->email ?? 'NULL'));
|
||||
} else {
|
||||
$this->info('No existing UserBusiness found for this period');
|
||||
}
|
||||
|
||||
if ($this->option('commissions')) {
|
||||
$this->line('');
|
||||
$this->info('Testing UserBusiness Commissions to Credit...');
|
||||
|
||||
if ($existingUserBusiness) {
|
||||
try {
|
||||
$userPaymentCredits = new UserPaymentCredits($month, $year);
|
||||
$ret = $userPaymentCredits->addUserCreditItem($existingUserBusiness);
|
||||
$this->info('UserBusinessCredit calculated:');
|
||||
$this->info('User ID: ' . $ret->user_id);
|
||||
$this->info('Team Commission: ' . $ret->commission_pp_total);
|
||||
$this->info('Shop Commission: ' . $ret->commission_shop_sales);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Error calculating commissions: ' . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
$this->warn('No UserBusiness record found, cannot calculate commissions.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->line('');
|
||||
$this->info('✅ Test completed successfully');
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Test failed with error: ' . $e->getMessage());
|
||||
$this->error('Stack trace: ' . $e->getTraceAsString());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,430 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\UserBusiness;
|
||||
use App\Models\UserLevel;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BusinessUpdateCalculatedFields extends Command
|
||||
{
|
||||
/**
|
||||
* php artisan business:update-calculated-fields {year} {month?}
|
||||
*
|
||||
* Aktualisiert bereits gespeicherte UserBusiness-Einträge mit den neuen berechneten Feldern:
|
||||
* - calc_qual_kp
|
||||
* - _calculated_qual_kp in Level-Arrays
|
||||
* - _calculated_payline_points_qual_kp in Level-Arrays
|
||||
*
|
||||
* Wenn kein Monat angegeben wird, werden alle 12 Monate des Jahres aktualisiert.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'business:update-calculated-fields {year} {month? : Optional - Wenn nicht angegeben, werden alle Monate des Jahres aktualisiert} {--dry-run : Zeige nur was gemacht werden würde, ohne zu speichern}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Aktualisiert gespeicherte UserBusiness-Einträge mit neuen berechneten Feldern für Level-Qualifikationen';
|
||||
|
||||
private $timeStart;
|
||||
private $month;
|
||||
private $year;
|
||||
private $isDryRun = false;
|
||||
private $processAllMonths = false;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$this->timeStart = microtime(true);
|
||||
$this->year = (int) $this->argument('year');
|
||||
$monthArg = $this->argument('month');
|
||||
$this->isDryRun = $this->option('dry-run');
|
||||
|
||||
if ($this->isDryRun) {
|
||||
$this->warn('DRY RUN MODE - Keine Änderungen werden gespeichert');
|
||||
}
|
||||
|
||||
// Prüfe ob ein Monat angegeben wurde
|
||||
if ($monthArg === null) {
|
||||
$this->processAllMonths = true;
|
||||
$this->info("Starte Update für ALLE MONATE des Jahres: {$this->year}");
|
||||
$this->info(str_repeat('=', 70));
|
||||
|
||||
return $this->processFullYear();
|
||||
} else {
|
||||
$this->month = (int) $monthArg;
|
||||
$this->info("Starte Update für Monat: {$this->month} | Jahr: {$this->year}");
|
||||
$this->logMemoryUsage('Command Start');
|
||||
|
||||
return $this->processSingleMonth();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Command failed with error: ' . $e->getMessage());
|
||||
$this->error('Stack trace: ' . $e->getTraceAsString());
|
||||
$this->logExecutionTime('COMMAND FAILED');
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verarbeite alle 12 Monate eines Jahres
|
||||
*/
|
||||
private function processFullYear(): int
|
||||
{
|
||||
$totalUpdated = 0;
|
||||
$totalSkipped = 0;
|
||||
$totalErrors = 0;
|
||||
$monthsProcessed = 0;
|
||||
$monthsFailed = 0;
|
||||
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$this->month = $month;
|
||||
|
||||
$this->newLine();
|
||||
$this->info("┌─────────────────────────────────────────────────────────────────────┐");
|
||||
$this->info("│ Verarbeite Monat: " . str_pad($month, 2, '0', STR_PAD_LEFT) . "/" . $this->year . str_repeat(' ', 51) . "│");
|
||||
$this->info("└─────────────────────────────────────────────────────────────────────┘");
|
||||
|
||||
try {
|
||||
$userBusinesses = $this->getUserBusinesses();
|
||||
|
||||
if ($userBusinesses->isEmpty()) {
|
||||
$this->warn(" Keine UserBusiness-Einträge für Monat {$month}/{$this->year} gefunden - überspringe");
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info(" Gefunden: {$userBusinesses->count()} UserBusiness-Einträge");
|
||||
|
||||
// Aktualisiere die Einträge
|
||||
$stats = $this->updateCalculatedFieldsWithStats($userBusinesses);
|
||||
|
||||
$totalUpdated += $stats['updated'];
|
||||
$totalSkipped += $stats['skipped'];
|
||||
$totalErrors += $stats['errors'];
|
||||
$monthsProcessed++;
|
||||
|
||||
$this->info(" ✓ Monat {$month} abgeschlossen: {$stats['updated']} aktualisiert, {$stats['skipped']} übersprungen, {$stats['errors']} Fehler");
|
||||
$this->logMemoryUsage("Nach Monat {$month}");
|
||||
} catch (\Exception $e) {
|
||||
$monthsFailed++;
|
||||
$this->error(" ✗ Fehler in Monat {$month}: " . $e->getMessage());
|
||||
\Log::error("BusinessUpdateCalculatedFields: Error in month {$month}/{$this->year}: " . $e->getMessage());
|
||||
// Weiter mit nächstem Monat
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Zusammenfassung für das ganze Jahr
|
||||
$this->newLine(2);
|
||||
$this->info(str_repeat('=', 70));
|
||||
$this->info("ZUSAMMENFASSUNG FÜR DAS JAHR {$this->year}:");
|
||||
$this->info(str_repeat('=', 70));
|
||||
$this->info("Verarbeitete Monate: {$monthsProcessed}/12");
|
||||
$this->info("Fehlgeschlagene Monate: {$monthsFailed}");
|
||||
$this->info("Gesamt aktualisiert: {$totalUpdated}");
|
||||
$this->info("Gesamt übersprungen: {$totalSkipped}");
|
||||
$this->info("Gesamt Fehler: {$totalErrors}");
|
||||
$this->info(str_repeat('=', 70));
|
||||
|
||||
$this->logExecutionTime('JAHRES-UPDATE ABGESCHLOSSEN');
|
||||
$this->logMemoryUsage('Command End');
|
||||
|
||||
return $monthsFailed > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verarbeite einen einzelnen Monat
|
||||
*/
|
||||
private function processSingleMonth(): int
|
||||
{
|
||||
// Hole alle UserBusiness-Einträge für den Monat
|
||||
$userBusinesses = $this->getUserBusinesses();
|
||||
|
||||
if ($userBusinesses->isEmpty()) {
|
||||
$this->error("Keine UserBusiness-Einträge für Monat {$this->month}/{$this->year} gefunden");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Gefunden: {$userBusinesses->count()} UserBusiness-Einträge");
|
||||
|
||||
// Aktualisiere die Einträge
|
||||
$this->updateCalculatedFields($userBusinesses);
|
||||
|
||||
$this->logExecutionTime('UPDATE COMPLETED SUCCESSFULLY');
|
||||
$this->logMemoryUsage('Command End');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hole alle UserBusiness-Einträge für den Monat
|
||||
*/
|
||||
private function getUserBusinesses()
|
||||
{
|
||||
return UserBusiness::where('month', $this->month)
|
||||
->where('year', $this->year)
|
||||
->orderBy('id', 'asc')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiere die berechneten Felder für alle UserBusiness-Einträge
|
||||
*/
|
||||
private function updateCalculatedFields($userBusinesses)
|
||||
{
|
||||
$stats = $this->updateCalculatedFieldsWithStats($userBusinesses);
|
||||
|
||||
$this->info("Update abgeschlossen:");
|
||||
$this->info(" - Aktualisiert: {$stats['updated']}");
|
||||
$this->info(" - Übersprungen: {$stats['skipped']}");
|
||||
$this->info(" - Fehler: {$stats['errors']}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiere die berechneten Felder und gebe Statistiken zurück
|
||||
*/
|
||||
private function updateCalculatedFieldsWithStats($userBusinesses): array
|
||||
{
|
||||
$bar = $this->output->createProgressBar($userBusinesses->count());
|
||||
$bar->start();
|
||||
|
||||
$updatedCount = 0;
|
||||
$skippedCount = 0;
|
||||
$errorCount = 0;
|
||||
|
||||
foreach ($userBusinesses as $userBusiness) {
|
||||
try {
|
||||
$updated = $this->updateSingleUserBusiness($userBusiness);
|
||||
|
||||
if ($updated) {
|
||||
$updatedCount++;
|
||||
} else {
|
||||
$skippedCount++;
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
|
||||
// Memory-Check alle 100 Einträge (nur wenn nicht ganzes Jahr)
|
||||
if (!$this->processAllMonths && ($updatedCount + $skippedCount) % 100 === 0) {
|
||||
$this->logMemoryUsage("Nach " . ($updatedCount + $skippedCount) . " Einträgen");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$errorCount++;
|
||||
$this->newLine();
|
||||
$this->warn("Fehler bei UserBusiness ID {$userBusiness->id}: " . $e->getMessage());
|
||||
\Log::error("BusinessUpdateCalculatedFields: Error for UserBusiness {$userBusiness->id}: " . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
return [
|
||||
'updated' => $updatedCount,
|
||||
'skipped' => $skippedCount,
|
||||
'errors' => $errorCount,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiere einen einzelnen UserBusiness-Eintrag
|
||||
*/
|
||||
private function updateSingleUserBusiness(UserBusiness $userBusiness): bool
|
||||
{
|
||||
$hasChanges = false;
|
||||
|
||||
// 1. Aktualisiere calc_qual_kp für qual_user_level
|
||||
if (!$userBusiness->calc_qual_kp) {
|
||||
$qualKp = $userBusiness->qual_user_level['qual_kp'] ?? null;
|
||||
|
||||
if ($qualKp !== null) {
|
||||
$rest_kp = max(0, $userBusiness->sales_volume_points_KP_sum - $qualKp);
|
||||
$calc_qual_kp = $rest_kp > 0 ? $qualKp : $userBusiness->sales_volume_points_KP_sum;
|
||||
|
||||
if ($userBusiness->calc_qual_kp !== $calc_qual_kp) {
|
||||
$userBusiness->calc_qual_kp = $calc_qual_kp;
|
||||
$hasChanges = true;
|
||||
}
|
||||
} else {
|
||||
$userBusiness->calc_qual_kp = $userBusiness->sales_volume_points_KP_sum;
|
||||
$hasChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Aktualisiere qual_user_level_next
|
||||
if (!empty($userBusiness->qual_user_level_next) && is_array($userBusiness->qual_user_level_next)) {
|
||||
$levelData = $userBusiness->qual_user_level_next;
|
||||
$updated = $this->addCalculatedFieldsToLevel($levelData, $userBusiness);
|
||||
|
||||
if ($updated !== false) {
|
||||
$userBusiness->qual_user_level_next = $updated;
|
||||
$hasChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Aktualisiere next_qual_user_level
|
||||
if (!empty($userBusiness->next_qual_user_level) && is_array($userBusiness->next_qual_user_level)) {
|
||||
$levelData = $userBusiness->next_qual_user_level;
|
||||
$updated = $this->addCalculatedFieldsToLevel($levelData, $userBusiness);
|
||||
|
||||
if ($updated !== false) {
|
||||
$userBusiness->next_qual_user_level = $updated;
|
||||
$hasChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Aktualisiere next_can_user_level
|
||||
if (!empty($userBusiness->next_can_user_level) && is_array($userBusiness->next_can_user_level)) {
|
||||
$levelData = $userBusiness->next_can_user_level;
|
||||
$updated = $this->addCalculatedFieldsToLevel($levelData, $userBusiness);
|
||||
|
||||
if ($updated !== false) {
|
||||
$userBusiness->next_can_user_level = $updated;
|
||||
$hasChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Speichere nur wenn Änderungen vorhanden sind und nicht im Dry-Run Mode
|
||||
if ($hasChanges && !$this->isDryRun) {
|
||||
$userBusiness->save();
|
||||
}
|
||||
|
||||
return $hasChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Füge berechnete Felder zu einem Level-Array hinzu
|
||||
*
|
||||
* @return array|false Array mit neuen Feldern oder false wenn keine Änderungen
|
||||
*/
|
||||
private function addCalculatedFieldsToLevel(array $levelData, UserBusiness $userBusiness)
|
||||
{
|
||||
// Prüfe ob Felder bereits existieren
|
||||
if (isset($levelData['_calculated_qual_kp']) && isset($levelData['_calculated_payline_points_qual_kp'])) {
|
||||
return false; // Keine Änderungen nötig
|
||||
}
|
||||
|
||||
$qualKp = $levelData['qual_kp'] ?? null;
|
||||
$paylines = $levelData['paylines'] ?? 0;
|
||||
|
||||
if ($qualKp === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Berechne die Werte
|
||||
$payline_points = $this->getPointsForPayline($userBusiness, $paylines);
|
||||
$rest_kp = max(0, $userBusiness->sales_volume_points_KP_sum - $qualKp);
|
||||
$payline_points_qual_kp = $payline_points + $rest_kp;
|
||||
$calc_qual_kp = $rest_kp > 0 ? $qualKp : $userBusiness->sales_volume_points_KP_sum;
|
||||
|
||||
// Füge die berechneten Felder hinzu
|
||||
$levelData['_calculated_qual_kp'] = $calc_qual_kp;
|
||||
$levelData['_calculated_payline_points'] = $payline_points;
|
||||
$levelData['_calculated_payline_points_qual_kp'] = $payline_points_qual_kp;
|
||||
|
||||
return $levelData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechne Payline-Punkte für eine bestimmte Anzahl von Paylines
|
||||
*/
|
||||
private function getPointsForPayline(UserBusiness $userBusiness, int $paylines): float
|
||||
{
|
||||
$payline_points = 0;
|
||||
$businessLines = $userBusiness->business_lines ?? [];
|
||||
|
||||
for ($i = 1; $i <= $paylines; $i++) {
|
||||
if (isset($businessLines[$i])) {
|
||||
$line = $businessLines[$i];
|
||||
|
||||
// Handle both array and object types
|
||||
if (is_array($line)) {
|
||||
$payline_points += (float) ($line['points'] ?? 0);
|
||||
} elseif (is_object($line)) {
|
||||
$payline_points += (float) ($line->points ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $payline_points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logge Ausführungszeit
|
||||
*/
|
||||
private function logExecutionTime($message)
|
||||
{
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info($message . ' | Zeit: ' . $sec . 'sec :' . round($micro * 1000, 4) . " ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* Logge Memory-Nutzung
|
||||
*/
|
||||
private function logMemoryUsage(string $checkpoint): void
|
||||
{
|
||||
$currentMemory = memory_get_usage();
|
||||
$peakMemory = memory_get_peak_usage();
|
||||
$memoryLimit = $this->parseMemoryLimit(ini_get('memory_limit'));
|
||||
|
||||
$currentFormatted = $this->formatBytes($currentMemory);
|
||||
$peakFormatted = $this->formatBytes($peakMemory);
|
||||
$limitFormatted = $this->formatBytes($memoryLimit);
|
||||
$usagePercent = round(($currentMemory / $memoryLimit) * 100, 2);
|
||||
|
||||
$this->info("[{$checkpoint}] Memory: {$currentFormatted} / {$limitFormatted} ({$usagePercent}%) | Peak: {$peakFormatted}");
|
||||
|
||||
if ($usagePercent > 80) {
|
||||
$this->warn("Hohe Memory-Nutzung bei {$checkpoint}: {$usagePercent}%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert Memory-Limit String zu Bytes
|
||||
*/
|
||||
private function parseMemoryLimit(string $limit): int
|
||||
{
|
||||
$limit = trim($limit);
|
||||
$last = strtolower($limit[strlen($limit) - 1]);
|
||||
$number = (int) $limit;
|
||||
|
||||
switch ($last) {
|
||||
case 'g':
|
||||
$number *= 1024;
|
||||
case 'm':
|
||||
$number *= 1024;
|
||||
case 'k':
|
||||
$number *= 1024;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert Bytes in lesbare Einheiten
|
||||
*/
|
||||
private function formatBytes(int $bytes, int $precision = 2): string
|
||||
{
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$i];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\User;
|
||||
use App\Services\Util;
|
||||
use App\Models\UserHistory;
|
||||
use App\Models\UserMessage;
|
||||
use App\Mail\MailCustomMessage;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
||||
class CheckPaymentsAccount extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'payments:check-accounts';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Überprüft Benutzer-Zahlungskonten und sendet Erinnerungen basierend auf Erneuerungsdaten.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::channel('cron')->info('COMMAND [payments:check-accounts] started.');
|
||||
$this->info('COMMAND [payments:check-accounts] started.');
|
||||
|
||||
// Die Logik wurde 1:1 aus der checkPaymentsAccounts-Methode übernommen
|
||||
$renewalDate = Carbon::now()->modify('+' . (config('mivita.remind_first_days') + 1) . ' days');
|
||||
Log::channel('cron')->info('Erneuerungsdatum für Zahlungen: ' . $renewalDate->format('Y-m-d H:i:s'));
|
||||
|
||||
$users = User::where('payment_account', '!=', NULL)
|
||||
->where('active', '=', 1)
|
||||
->where('blocked', '!=', 1)
|
||||
->where('payment_account', '<', $renewalDate)
|
||||
->get();
|
||||
|
||||
Log::channel('cron')->info('Found ' . $users->count() . ' users for payment reminders.');
|
||||
$this->info('Found ' . $users->count() . ' users for payment reminders.');
|
||||
|
||||
foreach ($users as $user) {
|
||||
Log::channel('cron')->info('Prüfe Zahlungserinnerungen für Benutzer: ' . $user->email);
|
||||
$this->checkReminderPayments($user);
|
||||
}
|
||||
|
||||
Log::channel('cron')->info('COMMAND [payments:check-accounts] finished.');
|
||||
$this->info('COMMAND [payments:check-accounts] finished.');
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/**
|
||||
* Überprüft und sendet Zahlungserinnerungen basierend auf Benutzerkontostand
|
||||
*
|
||||
* RULES:
|
||||
* > 21 remind_first_days = 31 reminder_first
|
||||
* > 21 remind_first_days + sepa = 32 reminder_first_sepa
|
||||
* > 14 remind_sec_days = 33 reminder_sec
|
||||
* > 2 remind_last_days = 34 reminder_last
|
||||
* > 0 deaktiv = 35 reminder_deaktiv
|
||||
* > 0 deaktiv + sepa = 36 reminder_deaktiv_sepa
|
||||
* == 7 abo_booking_days + sepa + cron = 37 reminder_collect_sepa
|
||||
*
|
||||
* @param User $user Benutzer
|
||||
* @return void
|
||||
*/
|
||||
|
||||
private function checkReminderPayments(User $user)
|
||||
{
|
||||
//35 reminder_deaktiv, 36 reminder_deaktiv_sepa
|
||||
if (!$user->isActiveAccount()) {
|
||||
Log::channel('cron')->info('Inaktives Konto für Benutzer: ' . $user->email);
|
||||
$this->checkIsReminderSend($user, 35);
|
||||
return;
|
||||
}
|
||||
|
||||
//34 reminder_last
|
||||
if ($user->daysActiveAccount() <= config('mivita.remind_last_days')) {
|
||||
Log::channel('cron')->info('Letzte Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')');
|
||||
$this->checkIsReminderSend($user, 34);
|
||||
return;
|
||||
}
|
||||
|
||||
//33 reminder_sec
|
||||
if ($user->daysActiveAccount() <= config('mivita.remind_sec_days')) {
|
||||
Log::channel('cron')->info('Zweite Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')');
|
||||
$this->checkIsReminderSend($user, 33);
|
||||
return;
|
||||
}
|
||||
|
||||
//31 reminder_first
|
||||
if ($user->daysActiveAccount() > config('mivita.remind_sec_days')) {
|
||||
Log::channel('cron')->info('Erste Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')');
|
||||
$this->checkIsReminderSend($user, 31);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Überprüft, ob eine Erinnerung bereits gesendet wurde
|
||||
*
|
||||
* @param User $user Benutzer
|
||||
* @param int $status Status-Code der Erinnerung
|
||||
* @return bool
|
||||
*/
|
||||
private function checkIsReminderSend(User $user, $status)
|
||||
{
|
||||
$isSend = UserHistory::whereUserId($user->id)
|
||||
->whereAction('reminder_payments')
|
||||
->whereIdentifier($user->payment_account)
|
||||
->whereStatus($status)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if ($isSend) {
|
||||
Log::channel('cron')->info('Erinnerung bereits gesendet für Benutzer: ' . $user->email . ' (Status: ' . $status . ')');
|
||||
return true;
|
||||
}
|
||||
|
||||
Log::channel('cron')->info('Sende neue Erinnerung für Benutzer: ' . $user->email . ' (Status: ' . $status . ')');
|
||||
$referenz = $this->sendReminderMail($user, $status);
|
||||
|
||||
UserHistory::create([
|
||||
'user_id' => $user->id,
|
||||
'action' => 'reminder_payments',
|
||||
'referenz' => $referenz,
|
||||
'identifier' => $user->payment_account,
|
||||
'status' => $status
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sendet eine Erinnerungs-E-Mail an den Benutzer
|
||||
*
|
||||
* @param User $user Benutzer
|
||||
* @param int $status Status-Code der Erinnerung
|
||||
* @return int
|
||||
*/
|
||||
private function sendReminderMail(User $user, $status)
|
||||
{
|
||||
$days = abs($user->daysActiveAccount());
|
||||
$pay_date = Carbon::parse($user->payment_account)->modify('- ' . config('mivita.abo_booking_days') . ' days')->format('d.m.Y');
|
||||
$datetime = $user->getPaymentAccountDateFormat();
|
||||
$price = "";
|
||||
|
||||
if ($user->payment_order_id && isset($user->payment_order_product->price)) {
|
||||
$price = 'von ' . $user->payment_order_product->getFormattedPrice() . ' EUR';
|
||||
}
|
||||
|
||||
$message = __('reminder.copy_first_' . $status, ['days' => $days, 'datetime' => $datetime, 'price' => $price, 'pay_date' => $pay_date]);
|
||||
$message_last = __('reminder.copy_last_' . $status, ['days' => $days, 'datetime' => $datetime, 'price' => $price, 'pay_date' => $pay_date]);
|
||||
$button = __('reminder.button_' . $status);
|
||||
|
||||
$message = preg_replace("/[\n\r]/", "", $message);
|
||||
$message_last = preg_replace("/[\n\r]/", "", $message_last);
|
||||
|
||||
$data = [
|
||||
'subject' => __('reminder.subject') . " | ID: " . $status,
|
||||
'message' => $message,
|
||||
'message_last' => $message_last,
|
||||
'url' => config('app.url') . '/user/membership',
|
||||
'button' => $button,
|
||||
];
|
||||
|
||||
$sender = User::find(1);
|
||||
$customer_mail = UserMessage::create(['user_id' => $user->id, 'send_user_id' => $sender->id, 'email' => $user->email, 'subject' => $data['subject'], 'message' => $data['message'] . " " . $data['message_last']]);
|
||||
|
||||
try {
|
||||
if (!Util::isTestSystem()) {
|
||||
if ($status >= 34) {
|
||||
Log::channel('cron')->info('Sende kritische Erinnerung mit BCC an: ' . $user->email);
|
||||
Mail::to($user->email)
|
||||
->locale($user->getLocale())
|
||||
->bcc(config('app.default_mail'))
|
||||
->send(new MailCustomMessage($user, $data, $sender, false));
|
||||
} else {
|
||||
Log::channel('cron')->info('Sende normale Erinnerung an: ' . $user->email);
|
||||
Mail::to($user->email)
|
||||
->locale($user->getLocale())
|
||||
->send(new MailCustomMessage($user, $data, $sender, false));
|
||||
}
|
||||
} else {
|
||||
Log::channel('cron')->info('Testsystem: E-Mail-Versand simuliert für: ' . $user->email);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::channel('cron')->error('Mail-Fehler für Benutzer ' . $user->email . ': ' . $e->getMessage());
|
||||
$customer_mail->fail = true;
|
||||
$customer_mail->error = $e->getMessage();
|
||||
$customer_mail->save();
|
||||
return 0;
|
||||
}
|
||||
|
||||
$customer_mail->send = true;
|
||||
$customer_mail->sent_at = now();
|
||||
$customer_mail->save();
|
||||
|
||||
Log::channel('cron')->info('Erinnerungsmail erfolgreich gesendet an: ' . $user->email);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Acme\Dhl\Models\DhlShipment;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DhlBackfillEmails extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'dhl:backfill-emails
|
||||
{--dry-run : Nur simulieren, keine Änderungen}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Füllt das E-Mail-Feld für bestehende DHL-Sendungen nach';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$dryRun = $this->option('dry-run');
|
||||
|
||||
$this->info('DHL E-Mail Backfill gestartet');
|
||||
$this->info('Modus: ' . ($dryRun ? 'DRY-RUN (keine Änderungen)' : 'LIVE'));
|
||||
$this->newLine();
|
||||
|
||||
// Hole alle Sendungen ohne E-Mail
|
||||
$shipments = DhlShipment::with('shoppingOrder.shopping_user')
|
||||
->whereNull('email')
|
||||
->orWhere('email', '')
|
||||
->get();
|
||||
|
||||
$total = $shipments->count();
|
||||
$updated = 0;
|
||||
$skipped = 0;
|
||||
|
||||
$this->info("Gefundene Sendungen ohne E-Mail: {$total}");
|
||||
$this->newLine();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
|
||||
foreach ($shipments as $shipment) {
|
||||
$bar->advance();
|
||||
|
||||
// Hole E-Mail aus Shopping User
|
||||
$email = null;
|
||||
if ($shipment->shoppingOrder && $shipment->shoppingOrder->shopping_user) {
|
||||
$email = $shipment->shoppingOrder->shopping_user->email;
|
||||
}
|
||||
|
||||
if (empty($email)) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $dryRun) {
|
||||
$shipment->email = $email;
|
||||
$shipment->save();
|
||||
}
|
||||
|
||||
$updated++;
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
// Statistik
|
||||
$this->info('Backfill abgeschlossen!');
|
||||
$this->newLine();
|
||||
$this->table(
|
||||
['Metrik', 'Anzahl'],
|
||||
[
|
||||
['Gesamt geprüft', $total],
|
||||
['E-Mail gesetzt', $updated],
|
||||
['Übersprungen (keine E-Mail)', $skipped],
|
||||
]
|
||||
);
|
||||
|
||||
if ($dryRun) {
|
||||
$this->warn('DRY-RUN: Keine Änderungen wurden vorgenommen.');
|
||||
$this->info('Führen Sie den Befehl ohne --dry-run aus, um die Änderungen zu speichern.');
|
||||
} else {
|
||||
$this->info("{$updated} Sendungen wurden aktualisiert.");
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,323 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Acme\Dhl\Models\DhlShipment;
|
||||
use App\Mail\MailDhlTracking;
|
||||
use App\Services\DhlTrackingService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class DhlUpdateTracking extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'dhl:update-tracking
|
||||
{--days=30 : Sendungen der letzten X Tage aktualisieren}
|
||||
{--send-emails : Automatisch E-Mails bei Transit-Status senden}
|
||||
{--dry-run : Nur simulieren, keine Änderungen}
|
||||
{--test-email= : Test-E-Mail an angegebene Adresse senden}
|
||||
{--order= : Nur für bestimmte Bestellung (Order-ID)}
|
||||
{--force : Intervall-Filter überspringen, alle aktiven Sendungen aktualisieren}
|
||||
{--stale-days=30 : Sendungen ohne Statusänderung nach X Tagen als abgeschlossen markieren}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Aktualisiert Tracking-Status für DHL Sendungen (status-basierte Intervalle, Batch-API)';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$days = (int) $this->option('days');
|
||||
$sendEmails = $this->option('send-emails');
|
||||
$dryRun = $this->option('dry-run');
|
||||
$testEmail = $this->option('test-email');
|
||||
$orderId = $this->option('order');
|
||||
$force = $this->option('force');
|
||||
$staleDays = (int) $this->option('stale-days');
|
||||
|
||||
$this->info('DHL Tracking Update gestartet');
|
||||
$this->info("Optionen: --days={$days}, --send-emails=".($sendEmails ? 'ja' : 'nein')
|
||||
.', --dry-run='.($dryRun ? 'ja' : 'nein')
|
||||
.', --force='.($force ? 'ja' : 'nein')
|
||||
.", --stale-days={$staleDays}");
|
||||
if ($testEmail) {
|
||||
$this->info("Test-Modus: E-Mails werden an {$testEmail} gesendet");
|
||||
}
|
||||
if ($orderId) {
|
||||
$this->info("Filter: Nur Order-ID {$orderId}");
|
||||
}
|
||||
$this->newLine();
|
||||
|
||||
// Step 1: Mark stale shipments as completed (before main query)
|
||||
$staleCompleted = $this->markStaleShipmentsCompleted($staleDays, $dryRun);
|
||||
|
||||
// Step 2: Build query for shipments that need tracking update
|
||||
$query = $this->buildShipmentQuery($days, $orderId, $force);
|
||||
|
||||
$shipments = $query->orderBy('created_at', 'desc')->get();
|
||||
|
||||
// Count total active shipments for statistics (before interval filter)
|
||||
$totalActive = DhlShipment::active()
|
||||
->whereNull('tracking_completed_at')
|
||||
->where('created_at', '>=', now()->subDays($days))
|
||||
->whereNotNull('dhl_shipment_no')
|
||||
->count();
|
||||
|
||||
$total = $shipments->count();
|
||||
$skippedByInterval = $totalActive - $total;
|
||||
|
||||
$this->info("Aktive Sendungen gesamt: {$totalActive}");
|
||||
$this->info('Übersprungen (Intervall): '.max(0, $skippedByInterval));
|
||||
$this->info("Zu aktualisieren: {$total}");
|
||||
|
||||
if ($total === 0) {
|
||||
$this->info('Keine Sendungen zum Aktualisieren gefunden.');
|
||||
$this->printSummary(0, ['updated' => 0, 'failed' => 0, 'completed' => 0, 'emails_sent' => 0, 'skipped' => 0], $staleCompleted, max(0, $skippedByInterval));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$trackingService = new DhlTrackingService;
|
||||
$stats = [
|
||||
'updated' => 0,
|
||||
'failed' => 0,
|
||||
'completed' => 0,
|
||||
'emails_sent' => 0,
|
||||
'skipped' => 0,
|
||||
];
|
||||
|
||||
if ($dryRun) {
|
||||
$stats['skipped'] = $total;
|
||||
$this->info("Dry-Run: {$total} Sendungen würden aktualisiert.");
|
||||
} else {
|
||||
// Collect old statuses for email decision
|
||||
$oldStatuses = $shipments->pluck('status', 'id')->toArray();
|
||||
|
||||
// Use batch API for efficient processing
|
||||
$this->info('Starte Batch-Tracking-Update...');
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$batchResult = $trackingService->updateTrackingBatch($shipments);
|
||||
|
||||
$stats['updated'] = $batchResult['updated'];
|
||||
$stats['failed'] = $batchResult['failed'];
|
||||
$stats['completed'] = $batchResult['completed'];
|
||||
|
||||
$bar->advance($total);
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
// Send tracking emails if enabled
|
||||
if ($sendEmails) {
|
||||
$this->info('Prüfe E-Mail-Versand...');
|
||||
|
||||
foreach ($shipments as $shipment) {
|
||||
$shipment->refresh();
|
||||
$oldStatus = $oldStatuses[$shipment->id] ?? '';
|
||||
|
||||
if ($this->shouldSendEmail($shipment, $oldStatus)) {
|
||||
try {
|
||||
$this->sendTrackingEmail($shipment, $testEmail);
|
||||
$stats['emails_sent']++;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('[DHL Cron] Failed to send tracking email', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->printSummary($total, $stats, $staleCompleted, max(0, $skippedByInterval));
|
||||
|
||||
Log::info('[DHL Cron] Tracking update completed', array_merge($stats, [
|
||||
'total' => $total,
|
||||
'stale_completed' => $staleCompleted,
|
||||
'skipped_interval' => max(0, $skippedByInterval),
|
||||
]));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the shipment query with or without interval filtering.
|
||||
*/
|
||||
private function buildShipmentQuery(int $days, ?string $orderId, bool $force)
|
||||
{
|
||||
if ($force) {
|
||||
// --force: Alle aktiven Sendungen ohne Intervall-Filter
|
||||
$query = DhlShipment::active()
|
||||
->whereNull('tracking_completed_at')
|
||||
->where('created_at', '>=', now()->subDays($days))
|
||||
->whereNotNull('dhl_shipment_no');
|
||||
} else {
|
||||
// Normal: Status-basierte Intervalle beachten
|
||||
$query = DhlShipment::needsTrackingUpdate()
|
||||
->where('created_at', '>=', now()->subDays($days));
|
||||
}
|
||||
|
||||
// Filter nach Order-ID wenn angegeben
|
||||
if ($orderId) {
|
||||
$query->where('order_id', $orderId);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark shipments as tracking-completed if they haven't changed status
|
||||
* for a given number of days (stale shipments).
|
||||
*/
|
||||
private function markStaleShipmentsCompleted(int $staleDays, bool $dryRun): int
|
||||
{
|
||||
$staleShipments = DhlShipment::active()
|
||||
->whereNull('tracking_completed_at')
|
||||
->whereNotNull('last_tracked_at')
|
||||
->where('last_tracked_at', '<', now()->subDays($staleDays))
|
||||
->where('created_at', '<', now()->subDays($staleDays))
|
||||
->get();
|
||||
|
||||
$count = $staleShipments->count();
|
||||
|
||||
if ($count > 0) {
|
||||
$this->warn("Veraltete Sendungen gefunden: {$count} (>{$staleDays} Tage ohne Änderung)");
|
||||
|
||||
if (! $dryRun) {
|
||||
foreach ($staleShipments as $shipment) {
|
||||
$shipment->markTrackingCompleted();
|
||||
|
||||
Log::info('[DHL Cron] Stale shipment tracking completed', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
'status' => $shipment->status,
|
||||
'last_tracked_at' => $shipment->last_tracked_at?->toDateTimeString(),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->info(" → {$count} Sendungen als Tracking-abgeschlossen markiert.");
|
||||
} else {
|
||||
$this->info(" → Dry-Run: {$count} Sendungen würden als abgeschlossen markiert.");
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the final summary table.
|
||||
*/
|
||||
private function printSummary(int $total, array $stats, int $staleCompleted, int $skippedByInterval): void
|
||||
{
|
||||
$this->info('Zusammenfassung:');
|
||||
$this->table(
|
||||
['Metrik', 'Anzahl'],
|
||||
[
|
||||
['Zu aktualisieren', $total],
|
||||
['Aktualisiert', $stats['updated']],
|
||||
['Fehlgeschlagen', $stats['failed']],
|
||||
['Tracking abgeschlossen', $stats['completed']],
|
||||
['E-Mails gesendet', $stats['emails_sent']],
|
||||
['Übersprungen (Dry-Run)', $stats['skipped']],
|
||||
['Übersprungen (Intervall)', $skippedByInterval],
|
||||
['Veraltet → abgeschlossen', $staleCompleted],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob eine E-Mail gesendet werden soll
|
||||
*/
|
||||
private function shouldSendEmail(DhlShipment $shipment, string $oldStatus): bool
|
||||
{
|
||||
// E-Mail nur senden wenn:
|
||||
// 1. Status ist jetzt "in_transit"
|
||||
// 2. Vorheriger Status war NICHT "in_transit" (also Status hat sich geändert)
|
||||
// 3. Noch keine E-Mail gesendet wurde
|
||||
return $shipment->status === 'in_transit'
|
||||
&& $oldStatus !== 'in_transit'
|
||||
&& ! $shipment->wasTrackingEmailSent()
|
||||
&& $shipment->canSendTrackingEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sendet die Tracking-E-Mail (mit Unterstützung für mehrere Sendungen pro Bestellung)
|
||||
*/
|
||||
private function sendTrackingEmail(DhlShipment $shipment, ?string $testEmail = null): void
|
||||
{
|
||||
try {
|
||||
$order = $shipment->shoppingOrder;
|
||||
|
||||
// Determine recipient email: test email > shipment email > shopping user email
|
||||
$recipientEmail = null;
|
||||
if ($testEmail) {
|
||||
$recipientEmail = $testEmail;
|
||||
} elseif (! empty($shipment->email)) {
|
||||
$recipientEmail = $shipment->email;
|
||||
} elseif ($order->shopping_user && ! empty($order->shopping_user->email)) {
|
||||
$recipientEmail = $order->shopping_user->email;
|
||||
}
|
||||
|
||||
if (! $recipientEmail) {
|
||||
Log::warning('[DHL Cron] Cannot send email - no recipient', [
|
||||
'shipment_id' => $shipment->id,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Sammle alle Sendungen für diese Bestellung, die noch keine E-Mail erhalten haben
|
||||
$allShipments = DhlShipment::where('order_id', $order->id)
|
||||
->where('status', 'in_transit')
|
||||
->whereNotNull('dhl_shipment_no')
|
||||
->whereNull('tracking_email_sent_at')
|
||||
->get();
|
||||
|
||||
// Wenn keine Sendungen gefunden, nutze nur die aktuelle
|
||||
if ($allShipments->isEmpty()) {
|
||||
$allShipments = collect([$shipment]);
|
||||
}
|
||||
|
||||
// Sende E-Mail mit allen Sendungen
|
||||
Mail::to($recipientEmail)->send(new MailDhlTracking($allShipments, $order));
|
||||
|
||||
// Markiere alle Sendungen als versendet
|
||||
foreach ($allShipments as $s) {
|
||||
$s->markTrackingEmailSent('auto');
|
||||
}
|
||||
|
||||
Log::info('[DHL Cron] Tracking email sent automatically', [
|
||||
'shipment_ids' => $allShipments->pluck('id')->toArray(),
|
||||
'shipments_count' => $allShipments->count(),
|
||||
'dhl_shipment_nos' => $allShipments->pluck('dhl_shipment_no')->toArray(),
|
||||
'email' => $recipientEmail,
|
||||
'is_test' => ! is_null($testEmail),
|
||||
]);
|
||||
|
||||
if ($allShipments->count() > 1) {
|
||||
$this->line(" → E-Mail mit {$allShipments->count()} Sendungen gesendet an: {$recipientEmail}");
|
||||
} else {
|
||||
$this->line(" → E-Mail gesendet an: {$recipientEmail}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('[DHL Cron] Failed to send tracking email', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\ShoppingInstance;
|
||||
use App\Models\ShoppingPayment;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class FixPaymentLinkStatus extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'payment:fix-link-status {--dry-run : Run without making changes}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Fix payment link status for paid orders that have incorrect status in shopping_instances';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$isDryRun = $this->option('dry-run');
|
||||
|
||||
if ($isDryRun) {
|
||||
$this->info(' DRY RUN MODE - No changes will be made');
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
$this->info('🔎 Searching for payment links with incorrect status...');
|
||||
$this->newLine();
|
||||
|
||||
// Find all ShoppingPayments with identifier that are paid
|
||||
$paidPayments = ShoppingPayment::whereNotNull('identifier')
|
||||
->whereHas('shopping_order', function ($query) {
|
||||
$query->where('paid', 1)
|
||||
->where('txaction', 'paid');
|
||||
})
|
||||
->with(['shopping_order'])
|
||||
->get();
|
||||
|
||||
$this->info("Found {$paidPayments->count()} paid payments with identifiers");
|
||||
$this->newLine();
|
||||
|
||||
$fixed = 0;
|
||||
$skipped = 0;
|
||||
$errors = 0;
|
||||
|
||||
foreach ($paidPayments as $payment) {
|
||||
$identifier = $payment->identifier;
|
||||
|
||||
// Find the corresponding ShoppingInstance
|
||||
$instance = ShoppingInstance::where('identifier', $identifier)->first();
|
||||
|
||||
if (! $instance) {
|
||||
$this->warn("⚠️ ShoppingInstance not found for identifier: {$identifier}");
|
||||
$errors++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if status needs to be updated
|
||||
if ($instance->status < 10) {
|
||||
$oldStatus = $instance->status;
|
||||
$oldStatusName = $instance->getStatus();
|
||||
|
||||
if (! $isDryRun) {
|
||||
$instance->status = 10; // link_paid
|
||||
$instance->save();
|
||||
}
|
||||
|
||||
$this->line(sprintf(
|
||||
'%s Payment #%d: %s → %s (Order #%d, Amount: %s)',
|
||||
$isDryRun ? '📋' : '✅',
|
||||
$payment->id,
|
||||
$oldStatusName." ($oldStatus)",
|
||||
'link_paid (10)',
|
||||
$payment->shopping_order_id,
|
||||
$payment->getPaymentAmount()
|
||||
));
|
||||
|
||||
$fixed++;
|
||||
} else {
|
||||
$skipped++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info('📊 Summary:');
|
||||
$this->table(
|
||||
['Status', 'Count'],
|
||||
[
|
||||
['Fixed/Would fix', $fixed],
|
||||
['Already correct', $skipped],
|
||||
['Errors', $errors],
|
||||
['Total processed', $paidPayments->count()],
|
||||
]
|
||||
);
|
||||
|
||||
if ($isDryRun && $fixed > 0) {
|
||||
$this->newLine();
|
||||
$this->warn('⚠️ This was a DRY RUN. Run without --dry-run to apply changes.');
|
||||
}
|
||||
|
||||
if (! $isDryRun && $fixed > 0) {
|
||||
$this->newLine();
|
||||
$this->info("✨ Successfully updated {$fixed} payment link(s)!");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Incentive;
|
||||
use App\Models\IncentiveParticipant;
|
||||
use App\Services\Incentive\IncentivePointsLogRepairService;
|
||||
use App\Services\Incentive\IncentiveTracker;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Batch-Neuberechnung fuer Incentives.
|
||||
*
|
||||
* Abo-Wertung (nicht hier codiert, sondern in {@see IncentiveParticipant::rebuildFromSourceTables}
|
||||
* und {@see IncentivePointsLogRepairService::syncMissingTrackingAbos}):
|
||||
*
|
||||
* - Eigenabo (me): zaehlt auch wenn es vor dem Qualifikationszeitraum abgeschlossen wurde;
|
||||
* Einmalpunkte wirken dann ab Qualifikationsbeginn (aktiviert_at/Log-Monat auf Start des Zeitraums).
|
||||
* - Kundenabo (ot): nur wenn im Qualifikationszeitraum neu abgeschlossen (created_at im Zeitraum).
|
||||
*
|
||||
* Zu Beginn werden fuer alle Berater (User mit m_level) fehlende Teilnehmerzeilen ohne
|
||||
* accepted_terms angelegt ({@see IncentiveParticipant::ensureConsultantsForIncentive}), damit
|
||||
* Punkte ohne Checkbox mitlaufen; die Rangliste blendet Namen erst nach Zustimmung ein.
|
||||
*/
|
||||
class IncentiveCalculate extends Command
|
||||
{
|
||||
protected $signature = 'incentive:calculate
|
||||
{incentive_id? : ID des Incentives (leer = alle aktiven)}
|
||||
{--force : Tracking-Tabellen + Log loeschen und komplett neu aufbauen}
|
||||
{--skip-repair : Kein Nachziehen von Trackings/FKs/SV-Logs (nur Summen aus bestehendem Log)}
|
||||
{--verbose-details : Zeigt Details pro Teilnehmer}';
|
||||
|
||||
protected $description = 'Incentive-Punkte: fehlende Partner-/Abo-Trackings, FK-Reparatur, fehlende SV-Logs, Summen/Ranking; --force = kompletter Neuaufbau aus Quelldaten';
|
||||
|
||||
public function handle(IncentivePointsLogRepairService $repairService): int
|
||||
{
|
||||
if ($id = $this->argument('incentive_id')) {
|
||||
$incentive = Incentive::find($id);
|
||||
if (! $incentive) {
|
||||
$this->error("Incentive #{$id} nicht gefunden.");
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
return $this->processIncentive($incentive, $repairService);
|
||||
}
|
||||
|
||||
$incentives = Incentive::active()->get();
|
||||
if ($incentives->isEmpty()) {
|
||||
$this->info('Keine aktiven Incentives gefunden.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$exitCode = self::SUCCESS;
|
||||
foreach ($incentives as $incentive) {
|
||||
if ($this->processIncentive($incentive, $repairService) !== self::SUCCESS) {
|
||||
$exitCode = self::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
private function processIncentive(Incentive $incentive, IncentivePointsLogRepairService $repairService): int
|
||||
{
|
||||
$force = $this->option('force');
|
||||
$skipRepair = $this->option('skip-repair');
|
||||
$verbose = $this->option('verbose-details');
|
||||
|
||||
$this->info("=== {$incentive->name} (ID: {$incentive->id}) ===");
|
||||
$this->info(" Zeitraum: {$incentive->qualification_start->format('d.m.Y')} - {$incentive->qualification_end->format('d.m.Y')}");
|
||||
if ($force) {
|
||||
$this->info(' Modus: FORCE (Tracking + Log aus Quelldaten neu aufbauen)');
|
||||
} elseif ($skipRepair) {
|
||||
$this->info(' Modus: Nur Neuberechnung (Summen/Ranking aus bestehendem Log)');
|
||||
} else {
|
||||
$this->info(' Modus: Tracking nachziehen + FK-Reparatur + SV-Logs + Neuberechnung');
|
||||
}
|
||||
|
||||
$stubAdded = IncentiveParticipant::ensureConsultantsForIncentive($incentive);
|
||||
if ($stubAdded > 0) {
|
||||
$this->info(" Berater-Teilnehmer neu angelegt (ohne Zustimmung): {$stubAdded}");
|
||||
}
|
||||
|
||||
$participants = $incentive->participants()->with('user', 'user.account')->get();
|
||||
$this->info(" Teilnehmer: {$participants->count()}");
|
||||
$this->newLine();
|
||||
|
||||
$stats = [
|
||||
'processed' => 0,
|
||||
'errors' => 0,
|
||||
'with_points' => 0,
|
||||
'with_partners' => 0,
|
||||
'with_abos' => 0,
|
||||
'tracking_partner_added' => 0,
|
||||
'tracking_abo_added' => 0,
|
||||
'repair_partner_fk' => 0,
|
||||
'repair_abo_fk' => 0,
|
||||
'repair_onetime_partner_fk' => 0,
|
||||
'repair_onetime_abo_fk' => 0,
|
||||
'sv_logs_added' => 0,
|
||||
];
|
||||
$errors = [];
|
||||
|
||||
$bar = $this->output->createProgressBar($participants->count());
|
||||
$bar->start();
|
||||
|
||||
foreach ($participants as $participant) {
|
||||
try {
|
||||
if (! $participant->user) {
|
||||
$bar->advance();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($force) {
|
||||
$participant->rebuildFromSourceTables()->save();
|
||||
} else {
|
||||
if (! $skipRepair) {
|
||||
$stats['tracking_partner_added'] += $repairService->syncMissingTrackingPartners($participant);
|
||||
$stats['tracking_abo_added'] += $repairService->syncMissingTrackingAbos($participant);
|
||||
$r = $repairService->repairForeignKeys($participant);
|
||||
$stats['repair_partner_fk'] += $r['partner_fk'];
|
||||
$stats['repair_abo_fk'] += $r['abo_fk'];
|
||||
$stats['repair_onetime_partner_fk'] += $r['onetime_partner_fk'];
|
||||
$stats['repair_onetime_abo_fk'] += $r['onetime_abo_fk'];
|
||||
$stats['sv_logs_added'] += $repairService->syncMissingSalesVolumeLogs($participant);
|
||||
}
|
||||
$participant->recalculateFromTrackingTables()->save();
|
||||
}
|
||||
|
||||
$stats['processed']++;
|
||||
|
||||
if ($participant->total_points > 0) {
|
||||
$stats['with_points']++;
|
||||
}
|
||||
if ($participant->qualified_partners > 0) {
|
||||
$stats['with_partners']++;
|
||||
}
|
||||
if ($participant->qualified_abos > 0) {
|
||||
$stats['with_abos']++;
|
||||
}
|
||||
|
||||
if ($verbose && $participant->total_points > 0) {
|
||||
$name = $participant->user->account
|
||||
? $participant->user->account->first_name.' '.$participant->user->account->last_name
|
||||
: ($participant->user->email ?? 'User #'.$participant->user_id);
|
||||
$bar->clear();
|
||||
$this->line(" {$name}: {$participant->total_points} Pkt, {$participant->qualified_partners} Partner, {$participant->qualified_abos} Abos");
|
||||
$bar->display();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$stats['errors']++;
|
||||
$errors[] = "Participant #{$participant->id} (User #{$participant->user_id}): {$e->getMessage()}";
|
||||
Log::error('IncentiveCalculation error for participant '.$participant->id.': '.$e->getMessage());
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
IncentiveTracker::updateRanking($incentive);
|
||||
$ranked = IncentiveParticipant::where('incentive_id', $incentive->id)
|
||||
->whereNotNull('rank')
|
||||
->count();
|
||||
|
||||
$tableRows = [
|
||||
['Verarbeitet', (string) $stats['processed']],
|
||||
['Fehler', (string) $stats['errors']],
|
||||
['Mit Punkten', (string) $stats['with_points']],
|
||||
['Mit Partnern', (string) $stats['with_partners']],
|
||||
['Mit Abos', (string) $stats['with_abos']],
|
||||
['Im Ranking', (string) $ranked],
|
||||
];
|
||||
|
||||
if (! $force) {
|
||||
$tableRows[] = ['Neupartner-Trackings nachgezogen', (string) $stats['tracking_partner_added']];
|
||||
$tableRows[] = ['Neuabo-Trackings nachgezogen', (string) $stats['tracking_abo_added']];
|
||||
$tableRows[] = ['FK Partner (akkum.) repariert', (string) $stats['repair_partner_fk']];
|
||||
$tableRows[] = ['FK Abo (akkum.) repariert', (string) $stats['repair_abo_fk']];
|
||||
$tableRows[] = ['FK Partner (Einmal) repariert', (string) $stats['repair_onetime_partner_fk']];
|
||||
$tableRows[] = ['FK Abo (Einmal) repariert', (string) $stats['repair_onetime_abo_fk']];
|
||||
$tableRows[] = ['Neue SV-Log-Eintraege', (string) $stats['sv_logs_added']];
|
||||
}
|
||||
|
||||
$this->table(['Metrik', 'Wert'], $tableRows);
|
||||
|
||||
if (! empty($errors)) {
|
||||
$this->error('Fehler:');
|
||||
foreach ($errors as $err) {
|
||||
$this->line(" - {$err}");
|
||||
}
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->info('Fertig.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Incentive;
|
||||
use App\Models\IncentiveNewPartner;
|
||||
use App\Models\IncentiveParticipant;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\User;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class IncentiveDebugTrackPartner extends Command
|
||||
{
|
||||
protected $signature = 'incentive:debug-track-partner {order_id : Shopping Order ID}';
|
||||
|
||||
protected $description = 'Debuggt trackNewPartner Schritt fuer Schritt fuer eine bestimmte Bestellung';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$order_id = $this->argument('order_id');
|
||||
$shopping_order = ShoppingOrder::find($order_id);
|
||||
|
||||
if (! $shopping_order) {
|
||||
$this->error("Shopping Order #{$order_id} nicht gefunden.");
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->info("=== Debug trackNewPartner fuer Order #{$order_id} ===");
|
||||
$this->newLine();
|
||||
|
||||
// 1. Bestelldaten
|
||||
$this->info('[1] Bestelldaten:');
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['id', $shopping_order->id],
|
||||
['auth_user_id', $shopping_order->auth_user_id ?? 'NULL'],
|
||||
['member_id', $shopping_order->member_id ?? 'NULL'],
|
||||
['payment_for', $shopping_order->payment_for],
|
||||
['paid', $shopping_order->paid],
|
||||
['txaction', $shopping_order->txaction],
|
||||
['mode', $shopping_order->mode],
|
||||
['created_at', $shopping_order->created_at],
|
||||
]);
|
||||
|
||||
// 2. Prüfe payment_for == 1 (Voraussetzung im Payment.php)
|
||||
if ($shopping_order->payment_for != 1) {
|
||||
$this->warn("[!] payment_for = {$shopping_order->payment_for} (nicht 1/registration). trackNewPartner wird nur bei payment_for=1 aufgerufen!");
|
||||
}
|
||||
|
||||
// 3. Neuer User
|
||||
$this->newLine();
|
||||
$this->info('[2] Neuer User (auth_user_id):');
|
||||
|
||||
if (! $shopping_order->auth_user_id) {
|
||||
$this->error(' auth_user_id ist NULL -> ABBRUCH (return)');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$new_user = User::find($shopping_order->auth_user_id);
|
||||
if (! $new_user) {
|
||||
$this->error(" User #{$shopping_order->auth_user_id} nicht gefunden -> ABBRUCH (return)");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['id', $new_user->id],
|
||||
['email', $new_user->email],
|
||||
['m_sponsor', $new_user->m_sponsor ?? 'NULL'],
|
||||
['active', $new_user->active],
|
||||
['created_at', $new_user->created_at],
|
||||
]);
|
||||
|
||||
if (! $new_user->m_sponsor) {
|
||||
$this->error(' m_sponsor ist NULL -> ABBRUCH (return)');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$sponsor_id = $new_user->m_sponsor;
|
||||
$this->info(" Sponsor ID: {$sponsor_id}");
|
||||
|
||||
// 4. Registration Date
|
||||
$registration_date = $shopping_order->created_at;
|
||||
$this->newLine();
|
||||
$this->info("[3] Registration Date: {$registration_date}");
|
||||
|
||||
// 5. Aktive Incentives
|
||||
$this->newLine();
|
||||
$this->info('[4] Aktive Incentives pruefen:');
|
||||
|
||||
$all_incentives = Incentive::query()->get();
|
||||
$this->info(" Incentives gesamt: {$all_incentives->count()}");
|
||||
|
||||
foreach ($all_incentives as $incentive) {
|
||||
$is_active = $incentive->status == 1;
|
||||
$in_range = $registration_date >= $incentive->qualification_start
|
||||
&& $registration_date <= $incentive->qualification_end;
|
||||
|
||||
$status_icon = $is_active ? 'AKTIV' : 'INAKTIV';
|
||||
$range_icon = $in_range ? 'IM ZEITRAUM' : 'AUSSERHALB';
|
||||
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['Incentive', "#{$incentive->id}: {$incentive->name}"],
|
||||
['Status', "{$incentive->status} ({$status_icon})"],
|
||||
['qualification_start', $incentive->qualification_start],
|
||||
['qualification_end', $incentive->qualification_end],
|
||||
['Registration Date', "{$registration_date} ({$range_icon})"],
|
||||
]);
|
||||
|
||||
if (! $is_active) {
|
||||
$this->warn(' -> Uebersprungen: Incentive nicht aktiv');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $in_range) {
|
||||
$this->warn(' -> Uebersprungen: Registration Date ausserhalb Qualifikationszeitraum');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info(" -> MATCH! Incentive #{$incentive->id} ist aktiv und Registration Date liegt im Zeitraum.");
|
||||
|
||||
// 6. Participant prüfen
|
||||
$this->newLine();
|
||||
$this->info("[5] Participant-Check: Sponsor #{$sponsor_id} in Incentive #{$incentive->id}");
|
||||
|
||||
$participant = IncentiveParticipant::where('incentive_id', $incentive->id)
|
||||
->where('user_id', $sponsor_id)
|
||||
->first();
|
||||
|
||||
if (! $participant) {
|
||||
$this->error(" Sponsor #{$sponsor_id} ist KEIN Teilnehmer in Incentive #{$incentive->id} -> SKIP");
|
||||
|
||||
// Zeige alle Teilnehmer-User-IDs
|
||||
$participant_ids = IncentiveParticipant::where('incentive_id', $incentive->id)
|
||||
->pluck('user_id')
|
||||
->toArray();
|
||||
$this->info(' Teilnehmer User-IDs: '.implode(', ', array_slice($participant_ids, 0, 20))
|
||||
.(count($participant_ids) > 20 ? '... (+'.count($participant_ids) - 20 .')' : ''));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info(" Participant gefunden: #{$participant->id} (User #{$participant->user_id})");
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['participant.id', $participant->id],
|
||||
['user_id', $participant->user_id],
|
||||
['total_points', $participant->total_points],
|
||||
['qualified_partners', $participant->qualified_partners],
|
||||
['accepted_terms_at', $participant->accepted_terms_at ?? 'NULL'],
|
||||
]);
|
||||
|
||||
// 7. Tracking-Eintrag prüfen
|
||||
$this->newLine();
|
||||
$this->info('[6] Tracking-Eintrag (incentive_new_partners):');
|
||||
|
||||
$existing = IncentiveNewPartner::where('participant_id', $participant->id)
|
||||
->where('user_id', $new_user->id)
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
$this->warn(" Eintrag existiert bereits: #{$existing->id} (erstellt: {$existing->created_at})");
|
||||
} else {
|
||||
$this->info(' Kein Eintrag vorhanden -> wuerde neu erstellt werden.');
|
||||
}
|
||||
|
||||
// 8. Zusammenfassung
|
||||
$this->newLine();
|
||||
$this->info('=== ERGEBNIS ===');
|
||||
$this->info('trackNewPartner WUERDE erfolgreich laufen fuer:');
|
||||
$this->info(" Neuer Partner: User #{$new_user->id} ({$new_user->email})");
|
||||
$this->info(" Sponsor/Teilnehmer: User #{$sponsor_id} (Participant #{$participant->id})");
|
||||
$this->info(" Incentive: #{$incentive->id} ({$incentive->name})");
|
||||
$this->info(" Einmalpunkte: {$incentive->points_partner_onetime}");
|
||||
}
|
||||
|
||||
// Prüfe den Query wie er im Code steht
|
||||
$this->newLine();
|
||||
$this->info('[7] Exakter Query wie im Code:');
|
||||
$matched_incentives = Incentive::query()
|
||||
->active()
|
||||
->where('qualification_start', '<=', $registration_date)
|
||||
->where('qualification_end', '>=', $registration_date)
|
||||
->get();
|
||||
$this->info(" Incentive::active()->where(start <= {$registration_date})->where(end >= {$registration_date})");
|
||||
$this->info(" Ergebnis: {$matched_incentives->count()} Incentive(s)");
|
||||
foreach ($matched_incentives as $mi) {
|
||||
$this->info(" -> #{$mi->id}: {$mi->name}");
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,241 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Incentive;
|
||||
use App\Models\IncentiveNewAbo;
|
||||
use App\Models\IncentiveNewPartner;
|
||||
use App\Models\IncentivePointsLog;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\UserSalesVolume;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class IncentiveDebugTrackSalesVolume extends Command
|
||||
{
|
||||
protected $signature = 'incentive:debug-track-sv {sv_id : UserSalesVolume ID}';
|
||||
|
||||
protected $description = 'Debuggt trackSalesVolume Schritt fuer Schritt fuer einen bestimmten SalesVolume-Eintrag';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$sv_id = $this->argument('sv_id');
|
||||
$usv = UserSalesVolume::find($sv_id);
|
||||
|
||||
if (! $usv) {
|
||||
$this->error("UserSalesVolume #{$sv_id} nicht gefunden.");
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->info("=== Debug trackSalesVolume fuer USV #{$sv_id} ===");
|
||||
$this->newLine();
|
||||
|
||||
// 1. SalesVolume-Daten
|
||||
$this->info('[1] SalesVolume-Daten:');
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['id', $usv->id],
|
||||
['user_id', $usv->user_id ?? 'NULL'],
|
||||
['shopping_order_id', $usv->shopping_order_id ?? 'NULL'],
|
||||
['user_invoice_id', $usv->user_invoice_id ?? 'NULL'],
|
||||
['month', $usv->month ?? 'NULL'],
|
||||
['year', $usv->year ?? 'NULL'],
|
||||
['points', $usv->getRawOriginal('points') ?? 'NULL'],
|
||||
['status', $usv->status.' ('.($usv->getStatusType() ?: '-').')'],
|
||||
['status_points', $usv->status_points ?? 'NULL'],
|
||||
['status_turnover', $usv->status_turnover ?? 'NULL'],
|
||||
['message', $usv->message ?? 'NULL'],
|
||||
]);
|
||||
|
||||
// 2. Fruehe Abbruch-Checks
|
||||
$month = $usv->month;
|
||||
$year = $usv->year;
|
||||
|
||||
if (! $month || ! $year) {
|
||||
$this->error('[ABBRUCH] month oder year ist NULL -> return');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$points = (int) abs($usv->getRawOriginal('points') ?? 0);
|
||||
if ($points <= 0) {
|
||||
$this->error("[ABBRUCH] points = {$points} (<= 0) -> return");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info(" Effektive Punkte: {$points}");
|
||||
|
||||
// 3. Aktive Incentives
|
||||
$this->newLine();
|
||||
$this->info('[2] Aktive Incentives:');
|
||||
$active_incentives = Incentive::query()->active()->get();
|
||||
$this->info(" Anzahl aktive: {$active_incentives->count()}");
|
||||
|
||||
foreach ($active_incentives as $incentive) {
|
||||
$in_scope = $incentive->isDateInScope($month, $year);
|
||||
$scope_label = $in_scope ? 'IM SCOPE' : 'AUSSERHALB';
|
||||
$this->info(" #{$incentive->id} {$incentive->name}: {$month}/{$year} -> {$scope_label}");
|
||||
$this->info(" Qualification: {$incentive->qualification_start} - {$incentive->qualification_end}, Calc End: {$incentive->calculation_end}");
|
||||
}
|
||||
|
||||
// ===== TEIL A: Neupartner-Check =====
|
||||
$this->newLine();
|
||||
$this->info('========================================');
|
||||
$this->info('[A] NEUPARTNER-CHECK: Ist User #'.$usv->user_id.' ein gettrackter Neupartner?');
|
||||
$this->info('========================================');
|
||||
|
||||
$partner_trackings = IncentiveNewPartner::where('user_id', $usv->user_id)
|
||||
->with('participant.incentive')
|
||||
->get();
|
||||
|
||||
$this->info(" IncentiveNewPartner-Eintraege fuer user_id={$usv->user_id}: {$partner_trackings->count()}");
|
||||
|
||||
if ($partner_trackings->isEmpty()) {
|
||||
$this->warn(' -> User ist KEIN gettrackter Neupartner in irgendeinem Incentive.');
|
||||
}
|
||||
|
||||
foreach ($partner_trackings as $tracking) {
|
||||
$participant = $tracking->participant;
|
||||
$incentive = $participant->incentive ?? null;
|
||||
|
||||
$this->newLine();
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['NewPartner #', $tracking->id],
|
||||
['participant_id', $tracking->participant_id],
|
||||
['Participant User', $participant->user_id],
|
||||
['Incentive', $incentive ? "#{$incentive->id}: {$incentive->name}" : 'NULL'],
|
||||
['Incentive Status', $incentive ? $incentive->status : 'NULL'],
|
||||
['Incentive aktiv?', $incentive && $incentive->status == 1 ? 'JA' : 'NEIN'],
|
||||
]);
|
||||
|
||||
if (! $incentive || $incentive->status != 1) {
|
||||
$this->warn(' -> Incentive nicht aktiv -> SKIP');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$in_scope = $incentive->isDateInScope($month, $year);
|
||||
$this->info(" isDateInScope({$month}, {$year}): ".($in_scope ? 'JA' : 'NEIN'));
|
||||
|
||||
if (! $in_scope) {
|
||||
$this->warn(' -> Monat/Jahr ausserhalb Scope -> SKIP');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Duplikat-Check
|
||||
$exists = IncentivePointsLog::where('participant_id', $participant->id)
|
||||
->where('user_sales_volume_id', $usv->id)
|
||||
->where('is_storno', false)
|
||||
->exists();
|
||||
|
||||
$this->info(' Log-Eintrag existiert bereits: '.($exists ? 'JA (Duplikat -> kein neuer Eintrag)' : 'NEIN -> wuerde erstellt'));
|
||||
|
||||
$this->info(' ==> MATCH! Punkte wuerden Participant #'.$participant->id." (User #{$participant->user_id}) gutgeschrieben");
|
||||
$this->info(" Typ: partner, Punkte: {$points} (accumulated)");
|
||||
}
|
||||
|
||||
// ===== TEIL B: Neuabo-Check =====
|
||||
$this->newLine();
|
||||
$this->info('========================================');
|
||||
$this->info('[B] NEUABO-CHECK: Stammt die Bestellung von einem gettrackten Abo-Kunden?');
|
||||
$this->info('========================================');
|
||||
|
||||
if (! $usv->shopping_order_id) {
|
||||
$this->warn(' shopping_order_id ist NULL -> Abo-Check uebersprungen.');
|
||||
} else {
|
||||
$order = ShoppingOrder::find($usv->shopping_order_id);
|
||||
|
||||
if (! $order) {
|
||||
$this->error(" ShoppingOrder #{$usv->shopping_order_id} nicht gefunden.");
|
||||
} else {
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['Order ID', $order->id],
|
||||
['shopping_user_id', $order->shopping_user_id ?? 'NULL'],
|
||||
['auth_user_id', $order->auth_user_id ?? 'NULL'],
|
||||
['member_id', $order->member_id ?? 'NULL'],
|
||||
['payment_for', $order->payment_for],
|
||||
['is_abo', $order->is_abo ? 'JA' : 'NEIN'],
|
||||
]);
|
||||
|
||||
if (! $order->shopping_user_id) {
|
||||
$this->warn(' shopping_user_id ist NULL -> kein Abo-Matching moeglich.');
|
||||
} else {
|
||||
$abo_trackings = IncentiveNewAbo::whereHas(
|
||||
'userAbo',
|
||||
fn ($q) => $q->where('shopping_user_id', $order->shopping_user_id)
|
||||
)
|
||||
->with('participant.incentive', 'userAbo')
|
||||
->get();
|
||||
|
||||
$this->info(" IncentiveNewAbo mit shopping_user_id={$order->shopping_user_id}: {$abo_trackings->count()}");
|
||||
|
||||
if ($abo_trackings->isEmpty()) {
|
||||
$this->warn(' -> Keine gettrackten Abos fuer diesen Kunden.');
|
||||
}
|
||||
|
||||
foreach ($abo_trackings as $tracking) {
|
||||
$participant = $tracking->participant;
|
||||
$incentive = $participant->incentive ?? null;
|
||||
$abo = $tracking->userAbo;
|
||||
|
||||
$this->newLine();
|
||||
$this->table(['Feld', 'Wert'], [
|
||||
['NewAbo #', $tracking->id],
|
||||
['user_abo_id', $tracking->user_abo_id],
|
||||
['Abo shopping_user_id', $abo ? $abo->shopping_user_id : 'NULL'],
|
||||
['participant_id', $tracking->participant_id],
|
||||
['Participant User', $participant->user_id],
|
||||
['Incentive', $incentive ? "#{$incentive->id}: {$incentive->name}" : 'NULL'],
|
||||
['Incentive aktiv?', $incentive && $incentive->status == 1 ? 'JA' : 'NEIN'],
|
||||
]);
|
||||
|
||||
if (! $incentive || $incentive->status != 1) {
|
||||
$this->warn(' -> Incentive nicht aktiv -> SKIP');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$in_scope = $incentive->isDateInScope($month, $year);
|
||||
$this->info(" isDateInScope({$month}, {$year}): ".($in_scope ? 'JA' : 'NEIN'));
|
||||
|
||||
if (! $in_scope) {
|
||||
$this->warn(' -> Monat/Jahr ausserhalb Scope -> SKIP');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$exists = IncentivePointsLog::where('participant_id', $participant->id)
|
||||
->where('user_sales_volume_id', $usv->id)
|
||||
->where('is_storno', false)
|
||||
->exists();
|
||||
|
||||
$this->info(' Log-Eintrag existiert bereits: '.($exists ? 'JA (Duplikat)' : 'NEIN -> wuerde erstellt'));
|
||||
|
||||
$this->info(' ==> MATCH! Punkte wuerden Participant #'.$participant->id." (User #{$participant->user_id}) gutgeschrieben");
|
||||
$this->info(" Typ: abo, Punkte: {$points} (accumulated)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Zusammenfassung =====
|
||||
$this->newLine();
|
||||
$this->info('=== ZUSAMMENFASSUNG ===');
|
||||
|
||||
$total_partner = $partner_trackings->filter(function ($t) use ($month, $year) {
|
||||
return $t->participant->incentive
|
||||
&& $t->participant->incentive->status == 1
|
||||
&& $t->participant->incentive->isDateInScope($month, $year);
|
||||
})->count();
|
||||
|
||||
$this->info(" Neupartner-Matches: {$total_partner}");
|
||||
$this->info(' Neuabo-Matches: siehe oben');
|
||||
|
||||
if ($total_partner === 0) {
|
||||
$this->warn(' -> Keine Punkte wuerden vergeben (kein Match).');
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class LogCleanup extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'logs:cleanup {--days=30 : Number of days to keep logs}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clean up old log files older than specified days';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$days = $this->option('days');
|
||||
$logPath = storage_path('logs');
|
||||
|
||||
if (!File::isDirectory($logPath)) {
|
||||
$this->error("Log directory not found: {$logPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$deletedFiles = 0;
|
||||
$deletedSize = 0;
|
||||
$cutoffDate = Carbon::now()->subDays($days);
|
||||
|
||||
$this->info("Cleaning up log files older than {$days} days (before {$cutoffDate->toDateString()})...");
|
||||
|
||||
$files = File::files($logPath);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$filename = $file->getFilename();
|
||||
|
||||
// Skip the current laravel.log file
|
||||
if ($filename === 'laravel.log') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lastModified = Carbon::createFromTimestamp($file->getMTime());
|
||||
|
||||
if ($lastModified->lt($cutoffDate)) {
|
||||
$fileSize = $file->getSize();
|
||||
|
||||
try {
|
||||
File::delete($file->getPathname());
|
||||
$deletedFiles++;
|
||||
$deletedSize += $fileSize;
|
||||
|
||||
$this->line("Deleted: {$filename} (" . $this->formatBytes($fileSize) . ", modified: {$lastModified->toDateString()})");
|
||||
} catch (\Exception $e) {
|
||||
$this->error("Failed to delete {$filename}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($deletedFiles > 0) {
|
||||
$this->info("\nCleanup complete!");
|
||||
$this->info("Deleted {$deletedFiles} file(s), freed " . $this->formatBytes($deletedSize));
|
||||
|
||||
Log::channel('cleanup')->info("Log cleanup completed", [
|
||||
'deleted_files' => $deletedFiles,
|
||||
'freed_space' => $this->formatBytes($deletedSize),
|
||||
'days' => $days
|
||||
]);
|
||||
} else {
|
||||
$this->info("No old log files found to delete.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format bytes to human readable format
|
||||
*
|
||||
* @param int $bytes
|
||||
* @return string
|
||||
*/
|
||||
private function formatBytes($bytes)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB'];
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
$bytes /= (1 << (10 * $pow));
|
||||
|
||||
return round($bytes, 2) . ' ' . $units[$pow];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,419 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class PayoneFailedPaypalReport extends Command
|
||||
{
|
||||
protected $signature = 'payone:failed-paypal-report
|
||||
{--from=2026-04-02 : Start-Datum (YYYY-MM-DD)}
|
||||
{--to= : End-Datum (YYYY-MM-DD), Standard: heute}
|
||||
{--output=storage/reports/paypal-failed-report.csv : Ausgabedatei}';
|
||||
|
||||
protected $description = 'Erstellt einen Schadenbericht über fehlgeschlagene PayPal-Zahlungen (Error 923)';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$from = $this->option('from');
|
||||
$to = $this->option('to') ?: now()->format('Y-m-d');
|
||||
$outputPath = $this->option('output');
|
||||
|
||||
$this->info("Schadenbericht PayPal-Ausfälle: {$from} bis {$to}");
|
||||
$this->newLine();
|
||||
|
||||
$orders = $this->getAffectedOrders($from, $to);
|
||||
|
||||
if ($orders->isEmpty()) {
|
||||
$this->warn('Keine fehlgeschlagenen PayPal-Zahlungen im angegebenen Zeitraum gefunden.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->displaySummary($orders, $from, $to);
|
||||
|
||||
$fullPath = base_path($outputPath);
|
||||
$dir = dirname($fullPath);
|
||||
if (! is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
|
||||
$this->writeCsvReport($fullPath, $orders, $from, $to);
|
||||
$this->writeTxtReport(str_replace('.csv', '.txt', $fullPath), $orders, $from, $to);
|
||||
$this->writeEmailLists($dir, $orders);
|
||||
|
||||
$this->newLine();
|
||||
$this->info("CSV-Bericht: {$fullPath}");
|
||||
$this->info('TXT-Bericht: ' . str_replace('.csv', '.txt', $fullPath));
|
||||
$this->info("E-Mail Berater: {$dir}/emails-berater.csv");
|
||||
$this->info("E-Mail Shop-Kunden: {$dir}/emails-shop-kunden.csv");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function getAffectedOrders(string $from, string $to): \Illuminate\Support\Collection
|
||||
{
|
||||
return DB::table('shopping_orders')
|
||||
->join('shopping_payments', function ($join) {
|
||||
$join->on('shopping_payments.shopping_order_id', '=', 'shopping_orders.id')
|
||||
->where('shopping_payments.clearingtype', '=', 'wlt')
|
||||
->where('shopping_payments.wallettype', '=', 'PPE');
|
||||
})
|
||||
->join('payment_transactions', function ($join) {
|
||||
$join->on('payment_transactions.shopping_payment_id', '=', 'shopping_payments.id')
|
||||
->where('payment_transactions.errorcode', '=', 923);
|
||||
})
|
||||
->join('shopping_users', 'shopping_users.id', '=', 'shopping_orders.shopping_user_id')
|
||||
->whereBetween('payment_transactions.created_at', ["{$from} 00:00:00", "{$to} 23:59:59"])
|
||||
->select(
|
||||
'shopping_orders.id as order_id',
|
||||
'shopping_orders.total_shipping',
|
||||
'shopping_orders.paid',
|
||||
'shopping_orders.txaction',
|
||||
'shopping_orders.mode',
|
||||
'shopping_orders.payment_for',
|
||||
'shopping_orders.auth_user_id',
|
||||
'shopping_orders.created_at as order_date',
|
||||
'shopping_users.billing_email',
|
||||
'shopping_users.billing_firstname',
|
||||
'shopping_users.billing_lastname',
|
||||
'shopping_payments.id as payment_id',
|
||||
'shopping_payments.reference',
|
||||
'shopping_payments.amount as amount_cents',
|
||||
'shopping_payments.currency',
|
||||
'payment_transactions.id as tx_id',
|
||||
'payment_transactions.errorcode',
|
||||
'payment_transactions.errormessage',
|
||||
'payment_transactions.created_at as error_date',
|
||||
)
|
||||
->orderBy('payment_transactions.created_at')
|
||||
->get();
|
||||
}
|
||||
|
||||
private function displaySummary(\Illuminate\Support\Collection $rows, string $from, string $to): void
|
||||
{
|
||||
$uniqueOrders = $rows->unique('order_id');
|
||||
$paidOrders = $uniqueOrders->where('paid', 1);
|
||||
$unpaidOrders = $uniqueOrders->where('paid', 0);
|
||||
|
||||
$this->table(
|
||||
['Kennzahl', 'Wert'],
|
||||
[
|
||||
['Zeitraum', "{$from} bis {$to}"],
|
||||
['Fehlgeschlagene Transaktionen (Error 923)', $rows->count()],
|
||||
['Betroffene Bestellungen (eindeutig)', $uniqueOrders->count()],
|
||||
['Davon nachträglich bezahlt (andere Zahlungsart)', $paidOrders->count()],
|
||||
['Nicht bezahlt (offen/verloren)', $unpaidOrders->count()],
|
||||
['Summe nicht bezahlter Bestellungen', number_format($unpaidOrders->sum('total_shipping'), 2, ',', '.') . ' EUR'],
|
||||
['Summe aller betroffenen Bestellungen', number_format($uniqueOrders->sum('total_shipping'), 2, ',', '.') . ' EUR'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function writeCsvReport(string $path, \Illuminate\Support\Collection $rows, string $from, string $to): void
|
||||
{
|
||||
$fp = fopen($path, 'w');
|
||||
|
||||
fprintf($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
|
||||
fputcsv($fp, [
|
||||
'Fehler-Datum',
|
||||
'Bestell-Nr',
|
||||
'Bestell-Datum',
|
||||
'Transaktions-ID',
|
||||
'Payment-Referenz',
|
||||
'Betrag (EUR)',
|
||||
'Fehlercode',
|
||||
'Fehlermeldung',
|
||||
'Modus',
|
||||
'Nachträglich bezahlt',
|
||||
'Aktueller Status',
|
||||
], ';');
|
||||
|
||||
$uniqueOrders = $rows->unique('order_id');
|
||||
$unpaidOrders = $uniqueOrders->where('paid', 0);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
fputcsv($fp, [
|
||||
$row->error_date,
|
||||
$row->order_id,
|
||||
$row->order_date,
|
||||
$row->tx_id,
|
||||
$row->reference,
|
||||
number_format($row->total_shipping, 2, ',', ''),
|
||||
$row->errorcode,
|
||||
$row->errormessage,
|
||||
$row->mode,
|
||||
$row->paid ? 'Ja' : 'Nein',
|
||||
$row->txaction,
|
||||
], ';');
|
||||
}
|
||||
|
||||
fputcsv($fp, [], ';');
|
||||
fputcsv($fp, ['ZUSAMMENFASSUNG'], ';');
|
||||
fputcsv($fp, ['Zeitraum', "{$from} bis {$to}"], ';');
|
||||
fputcsv($fp, ['Fehlgeschlagene Transaktionen', $rows->count()], ';');
|
||||
fputcsv($fp, ['Betroffene Bestellungen', $uniqueOrders->count()], ';');
|
||||
fputcsv($fp, ['Nicht bezahlt (offen/verloren)', $unpaidOrders->count()], ';');
|
||||
fputcsv($fp, ['Summe nicht bezahlter Bestellungen', number_format($unpaidOrders->sum('total_shipping'), 2, ',', '') . ' EUR'], ';');
|
||||
fputcsv($fp, ['Summe aller betroffenen Bestellungen', number_format($uniqueOrders->sum('total_shipping'), 2, ',', '') . ' EUR'], ';');
|
||||
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
private function writeTxtReport(string $path, \Illuminate\Support\Collection $rows, string $from, string $to): void
|
||||
{
|
||||
$uniqueOrders = $rows->unique('order_id');
|
||||
$paidOrders = $uniqueOrders->where('paid', 1);
|
||||
$unpaidOrders = $uniqueOrders->where('paid', 0);
|
||||
|
||||
$lines = [];
|
||||
$lines[] = '================================================================================';
|
||||
$lines[] = ' SCHADENBERICHT: Fehlgeschlagene PayPal-Zahlungen (PAYONE Error 923)';
|
||||
$lines[] = '================================================================================';
|
||||
$lines[] = '';
|
||||
$lines[] = "Zeitraum: {$from} bis {$to}";
|
||||
$lines[] = 'Erstellt am: ' . now()->format('d.m.Y H:i:s');
|
||||
$lines[] = 'Ursache: PayPal-Kontoverknüpfung bei PAYONE nicht migriert (Vertragsübernahme GmbH)';
|
||||
$lines[] = 'Portal-ID: 2030693';
|
||||
$lines[] = 'Merchant-ID: 42504';
|
||||
$lines[] = 'Sub-Account-ID: 43065';
|
||||
$lines[] = '';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = ' ZUSAMMENFASSUNG';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = '';
|
||||
$lines[] = sprintf(' Fehlgeschlagene Transaktionen (Error 923): %d', $rows->count());
|
||||
$lines[] = sprintf(' Betroffene Bestellungen (eindeutig): %d', $uniqueOrders->count());
|
||||
$lines[] = sprintf(' Davon nachträglich bezahlt (andere Methode): %d', $paidOrders->count());
|
||||
$lines[] = sprintf(' Nicht bezahlt (offen/verloren): %d', $unpaidOrders->count());
|
||||
$lines[] = '';
|
||||
$lines[] = sprintf(' Summe nicht bezahlter Bestellungen: %s EUR', number_format($unpaidOrders->sum('total_shipping'), 2, ',', '.'));
|
||||
$lines[] = sprintf(' Summe nachträglich bezahlter Bestellungen: %s EUR', number_format($paidOrders->sum('total_shipping'), 2, ',', '.'));
|
||||
$lines[] = sprintf(' Summe ALLER betroffenen Bestellungen: %s EUR', number_format($uniqueOrders->sum('total_shipping'), 2, ',', '.'));
|
||||
$lines[] = '';
|
||||
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = ' AUFSCHLÜSSELUNG NACH TAG';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = '';
|
||||
|
||||
$byDate = $rows->groupBy(fn($r) => substr($r->error_date, 0, 10));
|
||||
foreach ($byDate as $date => $dayRows) {
|
||||
$dayOrders = $dayRows->unique('order_id');
|
||||
$dayUnpaid = $dayOrders->where('paid', 0);
|
||||
$lines[] = sprintf(
|
||||
' %s: %3d Fehler | %3d Bestellungen | %3d nicht bezahlt | %s EUR offen',
|
||||
$date,
|
||||
$dayRows->count(),
|
||||
$dayOrders->count(),
|
||||
$dayUnpaid->count(),
|
||||
number_format($dayUnpaid->sum('total_shipping'), 2, ',', '.')
|
||||
);
|
||||
}
|
||||
|
||||
$lines[] = '';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = ' NICHT BEZAHLTE BESTELLUNGEN (DETAIL)';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = '';
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-20s %-18s %-14s %s',
|
||||
'Bestell-Nr',
|
||||
'Datum',
|
||||
'Referenz',
|
||||
'Betrag (EUR)',
|
||||
'Status'
|
||||
);
|
||||
$lines[] = ' ' . str_repeat('-', 80);
|
||||
|
||||
foreach ($unpaidOrders->sortBy('order_date') as $order) {
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-20s %-18s %14s %s',
|
||||
$order->order_id,
|
||||
$order->order_date,
|
||||
$order->reference,
|
||||
number_format($order->total_shipping, 2, ',', '.'),
|
||||
$order->txaction
|
||||
);
|
||||
}
|
||||
|
||||
$lines[] = ' ' . str_repeat('-', 80);
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-20s %-18s %14s',
|
||||
'GESAMT',
|
||||
'',
|
||||
$unpaidOrders->count() . ' Bestellungen',
|
||||
number_format($unpaidOrders->sum('total_shipping'), 2, ',', '.')
|
||||
);
|
||||
|
||||
$lines[] = '';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = ' NACHTRÄGLICH BEZAHLTE BESTELLUNGEN (andere Zahlungsart)';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = '';
|
||||
|
||||
if ($paidOrders->isEmpty()) {
|
||||
$lines[] = ' Keine.';
|
||||
} else {
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-20s %-18s %-14s %s',
|
||||
'Bestell-Nr',
|
||||
'Datum',
|
||||
'Referenz',
|
||||
'Betrag (EUR)',
|
||||
'Status'
|
||||
);
|
||||
$lines[] = ' ' . str_repeat('-', 80);
|
||||
|
||||
foreach ($paidOrders->sortBy('order_date') as $order) {
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-20s %-18s %14s %s',
|
||||
$order->order_id,
|
||||
$order->order_date,
|
||||
$order->reference,
|
||||
number_format($order->total_shipping, 2, ',', '.'),
|
||||
$order->txaction
|
||||
);
|
||||
}
|
||||
|
||||
$lines[] = ' ' . str_repeat('-', 80);
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-20s %-18s %14s',
|
||||
'GESAMT',
|
||||
'',
|
||||
$paidOrders->count() . ' Bestellungen',
|
||||
number_format($paidOrders->sum('total_shipping'), 2, ',', '.')
|
||||
);
|
||||
}
|
||||
|
||||
$lines[] = '';
|
||||
$this->appendEmailSectionToTxt($lines, $unpaidOrders);
|
||||
$lines[] = '';
|
||||
$lines[] = '================================================================================';
|
||||
$lines[] = ' Ende des Berichts';
|
||||
$lines[] = '================================================================================';
|
||||
$lines[] = '';
|
||||
|
||||
file_put_contents($path, implode("\n", $lines));
|
||||
}
|
||||
|
||||
private function appendEmailSectionToTxt(array &$lines, \Illuminate\Support\Collection $unpaidOrders): void
|
||||
{
|
||||
$berater = $unpaidOrders->filter(fn($o) => ! empty($o->auth_user_id))->sortBy('billing_email');
|
||||
$shopKunden = $unpaidOrders->filter(fn($o) => empty($o->auth_user_id))->sortBy('billing_email');
|
||||
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = ' BETROFFENE BERATER (mit Auth-User-ID) - nicht bezahlt';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = '';
|
||||
$lines[] = sprintf(' %-12s %-8s %-30s %-30s %14s', 'Bestell-Nr', 'User-ID', 'Name', 'E-Mail', 'Betrag (EUR)');
|
||||
$lines[] = ' ' . str_repeat('-', 100);
|
||||
|
||||
$beraterSum = 0;
|
||||
foreach ($berater as $order) {
|
||||
$name = trim(($order->billing_firstname ?? '') . ' ' . ($order->billing_lastname ?? ''));
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-8s %-30s %-30s %14s',
|
||||
$order->order_id,
|
||||
$order->auth_user_id,
|
||||
mb_substr($name, 0, 28),
|
||||
mb_substr($order->billing_email ?? '-', 0, 28),
|
||||
number_format($order->total_shipping, 2, ',', '.')
|
||||
);
|
||||
$beraterSum += $order->total_shipping;
|
||||
}
|
||||
|
||||
$lines[] = ' ' . str_repeat('-', 100);
|
||||
$lines[] = sprintf(' %-12s %-8s %-30s %-30s %14s', 'GESAMT', '', $berater->count() . ' Bestellungen', '', number_format($beraterSum, 2, ',', '.'));
|
||||
|
||||
$lines[] = '';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = ' BETROFFENE SHOP-KUNDEN (ohne Auth-User-ID) - nicht bezahlt';
|
||||
$lines[] = '--------------------------------------------------------------------------------';
|
||||
$lines[] = '';
|
||||
$lines[] = sprintf(' %-12s %-30s %-30s %14s', 'Bestell-Nr', 'Name', 'E-Mail', 'Betrag (EUR)');
|
||||
$lines[] = ' ' . str_repeat('-', 90);
|
||||
|
||||
$shopSum = 0;
|
||||
foreach ($shopKunden as $order) {
|
||||
$name = trim(($order->billing_firstname ?? '') . ' ' . ($order->billing_lastname ?? ''));
|
||||
$lines[] = sprintf(
|
||||
' %-12s %-30s %-30s %14s',
|
||||
$order->order_id,
|
||||
mb_substr($name, 0, 28),
|
||||
mb_substr($order->billing_email ?? '-', 0, 28),
|
||||
number_format($order->total_shipping, 2, ',', '.')
|
||||
);
|
||||
$shopSum += $order->total_shipping;
|
||||
}
|
||||
|
||||
$lines[] = ' ' . str_repeat('-', 90);
|
||||
$lines[] = sprintf(' %-12s %-30s %-30s %14s', 'GESAMT', $shopKunden->count() . ' Bestellungen', '', number_format($shopSum, 2, ',', '.'));
|
||||
}
|
||||
|
||||
private function writeEmailLists(string $dir, \Illuminate\Support\Collection $rows): void
|
||||
{
|
||||
$unpaidOrders = $rows->unique('order_id')->where('paid', 0);
|
||||
|
||||
$berater = $unpaidOrders->filter(fn($o) => ! empty($o->auth_user_id))->sortBy('order_date');
|
||||
$shopKunden = $unpaidOrders->filter(fn($o) => empty($o->auth_user_id))->sortBy('order_date');
|
||||
|
||||
$this->writeEmailCsv("{$dir}/emails-berater.csv", $berater, true);
|
||||
$this->writeEmailCsv("{$dir}/emails-shop-kunden.csv", $shopKunden, false);
|
||||
|
||||
$this->newLine();
|
||||
$this->table(
|
||||
['Kategorie', 'Bestellungen', 'Eindeutige E-Mails', 'Summe (EUR)'],
|
||||
[
|
||||
[
|
||||
'Berater (mit Auth-User-ID)',
|
||||
$berater->count(),
|
||||
$berater->pluck('billing_email')->filter()->unique()->count(),
|
||||
number_format($berater->sum('total_shipping'), 2, ',', '.') . ' EUR',
|
||||
],
|
||||
[
|
||||
'Shop-Kunden (ohne Auth-User-ID)',
|
||||
$shopKunden->count(),
|
||||
$shopKunden->pluck('billing_email')->filter()->unique()->count(),
|
||||
number_format($shopKunden->sum('total_shipping'), 2, ',', '.') . ' EUR',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function writeEmailCsv(string $path, \Illuminate\Support\Collection $orders, bool $includeUserId): void
|
||||
{
|
||||
$fp = fopen($path, 'w');
|
||||
fprintf($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
|
||||
$headers = ['Bestell-Nr', 'Bestell-Datum', 'Vorname', 'Nachname', 'E-Mail', 'Betrag (EUR)', 'Status'];
|
||||
if ($includeUserId) {
|
||||
array_splice($headers, 1, 0, 'Auth-User-ID');
|
||||
}
|
||||
fputcsv($fp, $headers, ';');
|
||||
|
||||
foreach ($orders as $order) {
|
||||
$row = [
|
||||
$order->order_id,
|
||||
$order->order_date,
|
||||
$order->billing_firstname ?? '',
|
||||
$order->billing_lastname ?? '',
|
||||
$order->billing_email ?? '',
|
||||
number_format($order->total_shipping, 2, ',', ''),
|
||||
$order->txaction,
|
||||
];
|
||||
if ($includeUserId) {
|
||||
array_splice($row, 1, 0, $order->auth_user_id);
|
||||
}
|
||||
fputcsv($fp, $row, ';');
|
||||
}
|
||||
|
||||
fputcsv($fp, [], ';');
|
||||
fputcsv($fp, ['GESAMT', '', '', '', $orders->count() . ' Bestellungen', number_format($orders->sum('total_shipping'), 2, ',', '') . ' EUR'], ';');
|
||||
fputcsv($fp, ['Eindeutige E-Mail-Adressen', $orders->pluck('billing_email')->filter()->unique()->count()], ';');
|
||||
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingPayment;
|
||||
use App\Services\AboHelper;
|
||||
use App\Services\Incentive\IncentiveTracker;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RepairMissingAboFromOrders extends Command
|
||||
{
|
||||
protected $signature = 'abo:repair-missing
|
||||
{--fix : Reparatur ausfuehren (ohne: nur Abgleich/Vorschau)}
|
||||
{--force : Mit --fix: ohne Rueckfrage (Skripte/CI)}
|
||||
{--since= : Nur Bestellungen mit created_at >= (Y-m-d)}
|
||||
{--until= : Nur Bestellungen mit created_at <= Ende dieses Tages (Y-m-d)}
|
||||
{--order= : Komma-getrennte shopping_order IDs (Filter)}
|
||||
{--mode=live : Modus: live, test, dev oder all}
|
||||
{--stats : Zusaetzliche Statistik: bezahlte Abo-Bestellungen vs. mit/ohne UserAboOrder}';
|
||||
|
||||
protected $description = 'Abgleich und Reparatur: bezahlte Abo-Bestellungen (Checkout) ohne Verknuepfung user_abo_orders — z. B. nach Payone-Callback vor Erfolgs-Redirect';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$missing = $this->queryMissingOrders()->orderBy('id')->get();
|
||||
|
||||
$this->info('Abgleich: Bestellungen mit is_abo, abo_interval>0, als bezahlt markiert, ohne user_abo_orders-Eintrag.');
|
||||
$this->newLine();
|
||||
|
||||
if ($this->option('stats')) {
|
||||
$this->printStats();
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
$this->info('Treffer (fehlende Verknuepfung): '.$missing->count());
|
||||
|
||||
if ($missing->isEmpty()) {
|
||||
$this->info('Keine Diskrepanz — nichts zu tun.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['ID', 'shopping_user_id', 'mode', 'txaction', 'paid', 'created_at'],
|
||||
$missing->take(200)->map(fn (ShoppingOrder $o) => [
|
||||
$o->id,
|
||||
$o->shopping_user_id,
|
||||
$o->mode,
|
||||
$o->txaction,
|
||||
$o->paid ? '1' : '0',
|
||||
$o->created_at?->format('Y-m-d H:i'),
|
||||
])
|
||||
);
|
||||
|
||||
if ($missing->count() > 200) {
|
||||
$this->warn('… und weitere '.($missing->count() - 200).' Eintraege (Ausgabe gekuerzt).');
|
||||
}
|
||||
|
||||
if (! $this->option('fix')) {
|
||||
$this->newLine();
|
||||
$this->warn('Trockenlauf. Nutze --fix zur Reparatur (mit Bestaetigung).');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if (! $this->option('force') && ! $this->confirm('Wirklich '.$missing->count().' Bestellung(en) reparieren?')) {
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$ok = 0;
|
||||
$fail = 0;
|
||||
$bar = $this->output->createProgressBar($missing->count());
|
||||
$bar->start();
|
||||
|
||||
foreach ($missing as $order) {
|
||||
try {
|
||||
DB::transaction(function () use ($order) {
|
||||
$this->repairSingleOrder($order);
|
||||
});
|
||||
$ok++;
|
||||
} catch (\Throwable $e) {
|
||||
$fail++;
|
||||
$this->newLine();
|
||||
$this->error("Order #{$order->id}: {$e->getMessage()}");
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
$this->info("Fertig: {$ok} repariert, {$fail} Fehler.");
|
||||
|
||||
return $fail > 0 ? self::FAILURE : self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Builder<ShoppingOrder>
|
||||
*/
|
||||
private function queryMissingOrders(): \Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
$q = ShoppingOrder::query()
|
||||
->where('is_abo', true)
|
||||
->where('abo_interval', '>', 0)
|
||||
->where(function ($sub) {
|
||||
$sub->where('paid', true)
|
||||
->orWhere('paid', 1);
|
||||
})
|
||||
->whereIn('txaction', ['paid', 'invoice_paid', 'extern_paid'])
|
||||
->whereNotNull('shopping_user_id')
|
||||
->whereHas('shopping_payments')
|
||||
->whereNotExists(function ($sub) {
|
||||
$sub->select(DB::raw('1'))
|
||||
->from('user_abo_orders')
|
||||
->whereColumn('user_abo_orders.shopping_order_id', 'shopping_orders.id');
|
||||
});
|
||||
|
||||
if ($ids = $this->parseOrderIds()) {
|
||||
$q->whereIn('id', $ids);
|
||||
}
|
||||
|
||||
if ($since = $this->option('since')) {
|
||||
$q->where('created_at', '>=', $since.' 00:00:00');
|
||||
}
|
||||
|
||||
if ($until = $this->option('until')) {
|
||||
$q->where('created_at', '<=', $until.' 23:59:59');
|
||||
}
|
||||
|
||||
$mode = (string) $this->option('mode');
|
||||
if ($mode !== 'all') {
|
||||
$q->where('mode', $mode);
|
||||
}
|
||||
|
||||
return $q;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
private function parseOrderIds(): array
|
||||
{
|
||||
$raw = $this->option('order');
|
||||
if ($raw === null || $raw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values(array_filter(array_map('intval', explode(',', (string) $raw))));
|
||||
}
|
||||
|
||||
private function printStats(): void
|
||||
{
|
||||
$mode = (string) $this->option('mode');
|
||||
$base = ShoppingOrder::query()
|
||||
->where('is_abo', true)
|
||||
->where('abo_interval', '>', 0)
|
||||
->where(function ($sub) {
|
||||
$sub->where('paid', true)->orWhere('paid', 1);
|
||||
})
|
||||
->whereIn('txaction', ['paid', 'invoice_paid', 'extern_paid']);
|
||||
|
||||
if ($since = $this->option('since')) {
|
||||
$base->where('created_at', '>=', $since.' 00:00:00');
|
||||
}
|
||||
if ($until = $this->option('until')) {
|
||||
$base->where('created_at', '<=', $until.' 23:59:59');
|
||||
}
|
||||
if ($mode !== 'all') {
|
||||
$base->where('mode', $mode);
|
||||
}
|
||||
if ($ids = $this->parseOrderIds()) {
|
||||
$base->whereIn('id', $ids);
|
||||
}
|
||||
|
||||
$totalPaidAbo = (clone $base)->count();
|
||||
|
||||
$withLink = (clone $base)->whereExists(function ($sub) {
|
||||
$sub->select(DB::raw('1'))
|
||||
->from('user_abo_orders')
|
||||
->whereColumn('user_abo_orders.shopping_order_id', 'shopping_orders.id');
|
||||
})->count();
|
||||
|
||||
$this->table(
|
||||
['Kennzahl', 'Anzahl'],
|
||||
[
|
||||
['Bezahlte Abo-Bestellungen (Filter)', $totalPaidAbo],
|
||||
['Davon mit user_abo_orders', $withLink],
|
||||
['Davon ohne user_abo_orders', max(0, $totalPaidAbo - $withLink)],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function repairSingleOrder(ShoppingOrder $order): void
|
||||
{
|
||||
$payment = ShoppingPayment::query()
|
||||
->where('shopping_order_id', $order->id)
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
if (! $payment) {
|
||||
throw new \RuntimeException('Kein ShoppingPayment zur Bestellung.');
|
||||
}
|
||||
|
||||
$order->loadMissing(['shopping_user', 'shopping_order_items']);
|
||||
$payment->loadMissing(['payment_transactions']);
|
||||
$payment->setRelation('shopping_order', $order);
|
||||
|
||||
AboHelper::createNewAbo($payment);
|
||||
|
||||
$order->refresh();
|
||||
|
||||
if (! $order->getUserAbo()) {
|
||||
throw new \RuntimeException('createNewAbo hat kein UserAbo erzeugt (pruefen: abo_interval, Bestellpositionen, ShoppingPayment.abo_interval).');
|
||||
}
|
||||
|
||||
AboHelper::setAboActive($order, 2, true);
|
||||
IncentiveTracker::trackAboActivated($order);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Services\BusinessPlan\SalesPointsVolume;
|
||||
use App\Services\Incentive\IncentiveTracker;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RepairMissingInvoices extends Command
|
||||
{
|
||||
protected $signature = 'repair:missing-invoices
|
||||
{--fix : Tatsaechlich reparieren (ohne Flag nur Vorschau)}
|
||||
{--no-mail : Keine Rechnungs-Mails versenden}
|
||||
{--since=2026-02-20 : Ab welchem Datum suchen}';
|
||||
|
||||
protected $description = 'Repariert fehlende Rechnungen und SalesVolumes fuer bezahlte Bestellungen (Bug: addSalesPointsVolumeUser)';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$since = $this->option('since') ?? '2026-03-16';
|
||||
$fix = $this->option('fix') ?? false;
|
||||
|
||||
$orders = ShoppingOrder::query()
|
||||
->where('mode', 'live')
|
||||
->where('paid', 0)
|
||||
->where('txaction', 'paid')
|
||||
->where('created_at', '>=', $since)
|
||||
->whereNull('deleted_at')
|
||||
->whereDoesntHave('user_invoice')
|
||||
->whereDoesntHave('user_sales_volume')
|
||||
// ->whereDoesntHave('shopping_payments', fn($q) => $q->where('clearingtype', 'vor'))
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
$this->info("Betroffene Bestellungen seit {$since}: {$orders->count()}");
|
||||
|
||||
if ($orders->isEmpty()) {
|
||||
$this->info('Keine betroffenen Bestellungen gefunden.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
// Zusammenfassung
|
||||
$total = $orders->sum('total');
|
||||
$byPaymentFor = $orders->groupBy('payment_for')->map->count();
|
||||
$this->table(
|
||||
['payment_for', 'Anzahl'],
|
||||
$byPaymentFor->map(fn ($count, $type) => [$type, $count])->values()
|
||||
);
|
||||
$this->info("Gesamtwert: {$total} EUR");
|
||||
|
||||
if (! $fix) {
|
||||
$this->warn('Trockenlauf! Nutze --fix um die Reparatur durchzufuehren.');
|
||||
$this->newLine();
|
||||
|
||||
// Erste 10 anzeigen
|
||||
$this->table(
|
||||
['ID', 'payment_for', 'total', 'txaction', 'created_at'],
|
||||
$orders->take(100)->map(fn ($o) => [
|
||||
$o->id,
|
||||
$o->payment_for,
|
||||
$o->total,
|
||||
$o->txaction,
|
||||
$o->created_at->format('Y-m-d H:i'),
|
||||
])
|
||||
);
|
||||
|
||||
if ($orders->count() > 100) {
|
||||
$this->info('... und '.($orders->count() - 100).' weitere');
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$send_mail = ! $this->option('no-mail');
|
||||
|
||||
if ($send_mail) {
|
||||
$this->info('Rechnungs-Mails werden versendet. Nutze --no-mail um dies zu unterdruecken.');
|
||||
} else {
|
||||
$this->warn('Rechnungs-Mails werden NICHT versendet.');
|
||||
}
|
||||
|
||||
if (! $this->confirm("Wirklich {$orders->count()} Bestellungen reparieren?")) {
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$errors = 0;
|
||||
$bar = $this->output->createProgressBar($orders->count());
|
||||
$bar->start();
|
||||
|
||||
foreach ($orders as $order) {
|
||||
try {
|
||||
// 1. SalesVolume erstellen
|
||||
$user_sales_volume = SalesPointsVolume::User($order);
|
||||
|
||||
// 2. Rechnung erstellen (mit Mail-Versand)
|
||||
$invoice_repo = new InvoiceRepository($order);
|
||||
$user_invoice = $invoice_repo->create([
|
||||
'invoice_send_mail' => $send_mail,
|
||||
]);
|
||||
|
||||
// 3. SalesVolume mit Rechnung verknuepfen
|
||||
$user_sales_volume->user_invoice_id = $user_invoice->id;
|
||||
$user_sales_volume->save();
|
||||
|
||||
// 4. Incentive tracking (falls relevant)
|
||||
IncentiveTracker::trackSalesVolume($user_sales_volume);
|
||||
|
||||
$success++;
|
||||
$this->info("Order #{$order->id}: Reparatur erfolgreich");
|
||||
} catch (\Throwable $e) {
|
||||
$errors++;
|
||||
$this->newLine();
|
||||
$this->error("Order #{$order->id}: {$e->getMessage()}");
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
$this->info("Fertig: {$success} repariert, {$errors} Fehler.");
|
||||
|
||||
return $errors > 0 ? self::FAILURE : self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,297 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Cron\UserMakeOrder;
|
||||
use App\Models\UserAbo;
|
||||
use App\Models\UserAboOrder;
|
||||
use App\Services\AboHelper;
|
||||
use App\Services\Incentive\IncentiveTracker;
|
||||
use App\Services\MyLog;
|
||||
use App\Services\Payment;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RetryFailedPaypalAbos extends Command
|
||||
{
|
||||
protected $signature = 'abo:retry-failed-paypal
|
||||
{--dry-run : Nur anzeigen, keine Bestellungen ausführen}
|
||||
{--abo-id= : Nur ein bestimmtes Abo erneut ausführen}';
|
||||
|
||||
protected $description = 'Führt Abo-Bestellungen erneut aus, die aufgrund der PayPal-Panne (Error 923) fehlgeschlagen sind';
|
||||
|
||||
private float $timeStart;
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->timeStart = microtime(true);
|
||||
$dryRun = $this->option('dry-run');
|
||||
$singleAboId = $this->option('abo-id');
|
||||
|
||||
\Log::channel('abo_order')->info('RetryFailedPaypalAbos: Gestartet', [
|
||||
'dry_run' => $dryRun,
|
||||
'abo_id' => $singleAboId,
|
||||
]);
|
||||
|
||||
$this->info($dryRun ? '=== DRY-RUN Modus (keine Bestellungen) ===' : '=== LIVE Modus ===');
|
||||
$this->newLine();
|
||||
|
||||
$abos = $this->getAffectedAbos($singleAboId);
|
||||
|
||||
if ($abos->isEmpty()) {
|
||||
$this->warn('Keine betroffenen PayPal-Abos gefunden.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->displayAboList($abos);
|
||||
|
||||
if (! $dryRun && ! $singleAboId) {
|
||||
if (! $this->confirm("Sollen alle {$abos->count()} Abos jetzt erneut ausgeführt werden?")) {
|
||||
$this->info('Abgebrochen.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
$results = ['success' => 0, 'error' => 0, 'skipped' => 0];
|
||||
|
||||
foreach ($abos as $userAbo) {
|
||||
if ($dryRun) {
|
||||
$this->info(" [DRY-RUN] Abo #{$userAbo->id} würde ausgeführt werden");
|
||||
$results['skipped']++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->retryAboOrder($userAbo);
|
||||
if ($result) {
|
||||
$results['success']++;
|
||||
} else {
|
||||
$results['error']++;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$results['error']++;
|
||||
\Log::channel('abo_order')->error('RetryFailedPaypalAbos: Exception', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
$this->error(" Abo #{$userAbo->id}: Exception - {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->table(
|
||||
['Ergebnis', 'Anzahl'],
|
||||
[
|
||||
['Erfolgreich', $results['success']],
|
||||
['Fehlgeschlagen', $results['error']],
|
||||
['Übersprungen (Dry-Run)', $results['skipped']],
|
||||
]
|
||||
);
|
||||
|
||||
$executionTime = $this->getExecutionTime();
|
||||
$this->info("Abgeschlossen in {$executionTime}");
|
||||
\Log::channel('abo_order')->info("RetryFailedPaypalAbos: Abgeschlossen in {$executionTime}", $results);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, UserAbo>
|
||||
*/
|
||||
private function getAffectedAbos(?string $singleAboId): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
$query = UserAbo::query()
|
||||
->where('status', 3)
|
||||
->where('active', true)
|
||||
->where('clearingtype', 'wlt')
|
||||
->where('wallettype', 'PPE')
|
||||
->whereRaw("DATE(next_date) = '2026-04-05'")
|
||||
->with(['shopping_user', 'user_abo_items']);
|
||||
|
||||
if ($singleAboId) {
|
||||
$query->where('id', $singleAboId);
|
||||
}
|
||||
|
||||
return $query->orderBy('id')->get();
|
||||
}
|
||||
|
||||
private function displayAboList(\Illuminate\Database\Eloquent\Collection $abos): void
|
||||
{
|
||||
$rows = $abos->map(fn (UserAbo $abo) => [
|
||||
$abo->id,
|
||||
$abo->user_id ?? '-',
|
||||
$abo->is_for,
|
||||
$abo->email,
|
||||
$abo->abo_interval,
|
||||
$abo->getRawOriginal('next_date'),
|
||||
$abo->user_abo_items->count().' Artikel',
|
||||
]);
|
||||
|
||||
$this->table(
|
||||
['Abo-ID', 'User-ID', 'Typ', 'E-Mail', 'Intervall', 'Next-Date', 'Artikel'],
|
||||
$rows->toArray()
|
||||
);
|
||||
|
||||
$this->info("Betroffene Abos: {$abos->count()}");
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
private function retryAboOrder(UserAbo $userAbo): bool
|
||||
{
|
||||
$this->info(" Verarbeite Abo #{$userAbo->id} ({$userAbo->email})...");
|
||||
|
||||
\Log::channel('abo_order')->info('RetryFailedPaypalAbos: Verarbeite Abo', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'email' => $userAbo->email,
|
||||
'payone_userid' => $userAbo->payone_userid,
|
||||
]);
|
||||
|
||||
$alreadyPaidToday = UserAboOrder::where('user_abo_id', $userAbo->id)
|
||||
->whereDate('created_at', now()->toDateString())
|
||||
->where('paid', true)
|
||||
->exists();
|
||||
|
||||
if ($alreadyPaidToday) {
|
||||
$this->warn(" Abo #{$userAbo->id}: Bereits heute bezahlt - übersprungen");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AboHelper::ensureUserAboItemsFromLatestOrder($userAbo);
|
||||
|
||||
$shoppingOrder = null;
|
||||
$userOrder = new UserMakeOrder($userAbo);
|
||||
|
||||
try {
|
||||
if (! $userOrder->createShoppingUser()) {
|
||||
$this->error(" Abo #{$userAbo->id}: Shopping-User konnte nicht erstellt werden");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$shoppingOrder = $userOrder->makeShoppingOrder();
|
||||
if (! $shoppingOrder) {
|
||||
$this->error(" Abo #{$userAbo->id}: Bestellung konnte nicht erstellt werden");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->info(" Bestellung #{$shoppingOrder->id} erstellt (Betrag: {$shoppingOrder->total_shipping} EUR)");
|
||||
|
||||
$response = $userOrder->makePayment();
|
||||
if (is_object($response)) {
|
||||
$response = (array) $response;
|
||||
}
|
||||
|
||||
if (! isset($response['status'])) {
|
||||
$this->error(" Abo #{$userAbo->id}: Ungültige Zahlungsantwort");
|
||||
$this->markAboError($userAbo, $shoppingOrder);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($response['status'] === 'APPROVED') {
|
||||
$this->info(" Zahlung ERFOLGREICH für Abo #{$userAbo->id}");
|
||||
$this->markAboSuccess($userAbo, $shoppingOrder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$errorCode = $response['errorcode'] ?? '-';
|
||||
$errorMsg = $response['errormessage'] ?? '-';
|
||||
$this->error(" Zahlung FEHLGESCHLAGEN für Abo #{$userAbo->id}: [{$errorCode}] {$errorMsg}");
|
||||
|
||||
MyLog::writeLog(
|
||||
'userabo',
|
||||
'error',
|
||||
'Error:RetryPaypal RetryFailedPaypalAbos / makePayment Error',
|
||||
$response
|
||||
);
|
||||
|
||||
$this->markAboError($userAbo, $shoppingOrder);
|
||||
|
||||
$shoppingPayment = $userOrder->getShoppingPayment();
|
||||
if ($shoppingPayment) {
|
||||
Payment::paymentStatusSendMail($shoppingOrder, $shoppingPayment, [
|
||||
'mode' => $shoppingPayment->mode,
|
||||
'txaction' => 'error',
|
||||
'send_link' => false,
|
||||
'payment_error' => $response,
|
||||
]);
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (\Throwable $e) {
|
||||
\Log::channel('abo_order')->error('RetryFailedPaypalAbos: Exception bei Abo-Verarbeitung', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
$this->error(" Exception: {$e->getMessage()}");
|
||||
|
||||
if ($shoppingOrder) {
|
||||
$this->markAboError($userAbo, $shoppingOrder);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function markAboSuccess(UserAbo $userAbo, $shoppingOrder): void
|
||||
{
|
||||
DB::transaction(function () use ($userAbo, $shoppingOrder) {
|
||||
$nextDate = AboHelper::setNextDate(now(), $userAbo->abo_interval);
|
||||
|
||||
$userAbo->update([
|
||||
'status' => 2,
|
||||
'next_date' => $nextDate,
|
||||
'last_date' => now(),
|
||||
]);
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => 1,
|
||||
'paid' => true,
|
||||
]);
|
||||
});
|
||||
|
||||
IncentiveTracker::trackAboActivated($shoppingOrder);
|
||||
|
||||
$nextDateFormatted = Carbon::parse($userAbo->getRawOriginal('next_date'))->format('d.m.Y');
|
||||
$this->info(" Status → 2 (abo_okay), nächstes Datum → {$nextDateFormatted}");
|
||||
|
||||
\Log::channel('abo_order')->info('RetryFailedPaypalAbos: Abo erfolgreich reaktiviert', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'next_date' => $userAbo->getRawOriginal('next_date'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function markAboError(UserAbo $userAbo, $shoppingOrder): void
|
||||
{
|
||||
DB::transaction(function () use ($userAbo, $shoppingOrder) {
|
||||
$userAbo->update(['last_date' => now()]);
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => 3,
|
||||
'paid' => false,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function getExecutionTime(): string
|
||||
{
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
return $sec.' Sekunden und '.round($micro * 1000, 2).' ms';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,664 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Http\Controllers\Api\KasController;
|
||||
use App\Http\Controllers\Api\KasSLLController;
|
||||
use App\Models\UserShop;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SubDomains extends Command
|
||||
{
|
||||
/**
|
||||
* Die Signatur des Konsolenbefehls.
|
||||
*
|
||||
* Aufruf: php artisan subdomains:action --force --start=4 --debug
|
||||
* /usr/bin/php82 artisan subdomains:action --force --start=4 --create-missing --debug
|
||||
* /usr/bin/php82 artisan subdomains:action --force --debug
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'subdomains:action {user_id?} {--force} {--start=1} {--create-missing} {--debug}';
|
||||
|
||||
/**
|
||||
* Die Beschreibung des Konsolenbefehls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Passt Parameter für die User-Subdomains an (PHP-Version, SSL)';
|
||||
|
||||
/**
|
||||
* Zeitstempel für die Ausführungszeit-Messung
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
private $timeStart;
|
||||
|
||||
/**
|
||||
* Standard-Domain für alle Subdomains
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $domain = 'mivita.care';
|
||||
|
||||
/**
|
||||
* Zu überspringende Subdomain-Präfixe
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $skipPrefixes = ['www.', 'api.', 'checkout.', 'preview.'];
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Befehlsinstanz.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt den Konsolenbefehl aus.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->timeStart = microtime(true);
|
||||
|
||||
$userId = $this->argument('user_id');
|
||||
$force = $this->option('force');
|
||||
$startId = $this->option('start');
|
||||
$createMissing = $this->option('create-missing');
|
||||
$debug = $this->option('debug');
|
||||
|
||||
if ($debug) {
|
||||
$this->warn("DEBUG-MODUS (DRY-RUN): Es werden keine tatsächlichen Änderungen vorgenommen!");
|
||||
}
|
||||
|
||||
$this->info("Starte Subdomain-Verwaltung" . ($force ? " (erzwungener Modus)" : ""));
|
||||
|
||||
try {
|
||||
if ($userId) {
|
||||
$this->info("Verarbeite einzelnen Benutzer mit ID: {$userId}");
|
||||
$result = $this->syncSingleUser($userId, $force, $createMissing, $debug);
|
||||
|
||||
if ($result) {
|
||||
$this->info("Benutzer {$userId} erfolgreich synchronisiert" . ($debug ? " (simuliert)" : ""));
|
||||
} else {
|
||||
$this->warn("Benutzer {$userId} konnte nicht vollständig synchronisiert werden" . ($debug ? " (simuliert)" : ""));
|
||||
}
|
||||
} else {
|
||||
$this->info("Verarbeite alle Benutzer ab ID: {$startId}");
|
||||
$result = $this->syncAllUsers($force, $startId, $createMissing, $debug);
|
||||
|
||||
// Zusammenfassung der Ergebnisse
|
||||
$this->displaySummary($result, $debug);
|
||||
}
|
||||
|
||||
$this->logExecutionTime("Subdomain-Verwaltung abgeschlossen" . ($debug ? " (DEBUG-MODUS)" : ""));
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
$this->error("Ein Fehler ist aufgetreten: " . $e->getMessage());
|
||||
Log::error("Shopping User Sync Fehler: ", [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'user_id' => $userId,
|
||||
'force' => $force,
|
||||
'start_id' => $startId,
|
||||
'debug' => $debug
|
||||
]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Zeigt eine Zusammenfassung der Synchronisationsergebnisse an
|
||||
*
|
||||
* @param array $result Ergebnisdaten der Synchronisation
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return void
|
||||
*/
|
||||
private function displaySummary($result, $debug = false)
|
||||
{
|
||||
$this->line("");
|
||||
$this->info("=== Zusammenfassung " . ($debug ? "(DEBUG-MODUS)" : "") . " ===");
|
||||
$this->info("Verarbeitete Shops: " . count($result['shops']));
|
||||
$this->info("Aktualisierte PHP-Versionen: " . $result['updatedCount'] . ($debug ? " (simuliert)" : ""));
|
||||
$this->info("Aktivierte SSL-Zertifikate: " . $result['sslEnabledCount'] . ($debug ? " (simuliert)" : ""));
|
||||
$this->info("Aktualisierte SSL-Konfigurationen: " . $result['sslConfiguredCount'] . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if (!empty($result['createdSubdomains'])) {
|
||||
$this->info("Neu erstellte Subdomains: " . count($result['createdSubdomains']) . ($debug ? " (simuliert)" : ""));
|
||||
}
|
||||
|
||||
if (!empty($result['missingSubdomains'])) {
|
||||
$this->warn("Fehlende Subdomains: " . count($result['missingSubdomains']));
|
||||
}
|
||||
|
||||
if (!empty($result['unusedSubdomains'])) {
|
||||
$this->warn("Ungenutzte Subdomains: " . count($result['unusedSubdomains']));
|
||||
}
|
||||
|
||||
if (!empty($result['doubleDomains'])) {
|
||||
$this->warn("Benutzer mit mehreren Shops: " . count($result['doubleDomains']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisiert einen einzelnen Benutzer und seine Shops
|
||||
*
|
||||
* @param int $userId Benutzer-ID
|
||||
* @param bool $force Erzwingt die Aktualisierung aller Subdomains
|
||||
* @param bool $createMissing Erstellt fehlende Subdomains
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return bool Erfolg der Operation
|
||||
*/
|
||||
private function syncSingleUser($userId, $force = false, $createMissing = false, $debug = false)
|
||||
{
|
||||
$this->info("Synchronisiere Benutzer mit ID: {$userId}");
|
||||
|
||||
// Benutzer-Shops abrufen
|
||||
$userShops = UserShop::where('user_id', $userId)->get();
|
||||
|
||||
if ($userShops->isEmpty()) {
|
||||
$this->warn("Keine Shops für Benutzer {$userId} gefunden");
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->info("Gefundene Shops für Benutzer {$userId}: " . $userShops->count());
|
||||
|
||||
// Subdomains abrufen und filtern
|
||||
$subdomains = $this->getFilteredSubdomains();
|
||||
$success = true;
|
||||
|
||||
// Benutzer-Shops durchlaufen und mit Subdomains abgleichen
|
||||
foreach ($userShops as $userShop) {
|
||||
$fullDomainName = $userShop->slug . '.' . $this->domain;
|
||||
$this->info("Verarbeite Shop: {$fullDomainName}");
|
||||
|
||||
// Prüfen, ob Subdomain existiert
|
||||
if (array_key_exists($fullDomainName, $subdomains)) {
|
||||
$success = $this->processExistingSubdomain($userShop, $subdomains[$fullDomainName], $force, $debug) && $success;
|
||||
} else {
|
||||
// Subdomain fehlt
|
||||
$this->warn("Shop {$userShop->slug}: Keine Subdomain gefunden");
|
||||
|
||||
// Optional: Neue Subdomain erstellen
|
||||
if ($createMissing) {
|
||||
$this->info("Erstelle fehlende Subdomain für Shop {$userShop->slug}" . ($debug ? " (simuliert)" : ""));
|
||||
$success = $this->createSubdomain($userShop->slug, $debug) && $success;
|
||||
} else {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verarbeitet eine existierende Subdomain und aktualisiert sie bei Bedarf
|
||||
*
|
||||
* @param UserShop $userShop Shop-Objekt
|
||||
* @param array $subdomainInfo Subdomain-Informationen
|
||||
* @param bool $force Erzwingt die Aktualisierung
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return bool Erfolg der Operation
|
||||
*/
|
||||
private function processExistingSubdomain($userShop, $subdomainInfo, $force, $debug = false)
|
||||
{
|
||||
$success = true;
|
||||
$hasSSL = ($subdomainInfo['ssl_certificate_sni'] === 'Y' ||
|
||||
$subdomainInfo['ssl_proxy'] === 'Y');
|
||||
$sslActive = ($subdomainInfo['ssl_certificate_sni_is_active'] ?? 'N') === 'Y';
|
||||
$phpVersion = $subdomainInfo['php_version'];
|
||||
|
||||
$this->info("Shop {$userShop->slug}: PHP-Version: {$phpVersion}, SSL: " .
|
||||
($hasSSL ? "Aktiviert" : "Nicht aktiviert") .
|
||||
($hasSSL ? ", SSL aktiv: " . ($sslActive ? "Ja" : "Nein") : ""));
|
||||
|
||||
// Prüfen, ob PHP-Version aktualisiert werden muss
|
||||
$requiredPhpVersion = config('app.php_version');
|
||||
if ($force || $phpVersion !== $requiredPhpVersion) {
|
||||
$this->info("Shop {$userShop->slug}: PHP-Version aktualisieren von {$phpVersion} auf {$requiredPhpVersion}" . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if (!$debug && !$this->updateSubdomainParams($userShop->slug, $requiredPhpVersion)) {
|
||||
$this->error("PHP-Version für {$userShop->slug}.{$this->domain} konnte nicht aktualisiert werden");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Prüfen, ob SSL aktiviert werden muss
|
||||
if ($force || !$hasSSL) {
|
||||
$this->info("Shop {$userShop->slug}: SSL aktivieren" . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if (!$debug && !$this->enableSSL($userShop->slug)) {
|
||||
$this->error("SSL für {$userShop->slug}.{$this->domain} konnte nicht aktiviert werden");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
// Prüfen, ob SSL-Konfiguration aktualisiert werden muss
|
||||
else if ($force || ($hasSSL && !$sslActive)) {
|
||||
$this->info("Shop {$userShop->slug}: SSL-Konfiguration aktualisieren" . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if (!$debug && !$this->updateSSL($userShop->slug . '.' . $this->domain)) {
|
||||
$this->error("SSL-Konfiguration für {$userShop->slug}.{$this->domain} konnte nicht aktualisiert werden");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisiert alle Benutzer-Shops
|
||||
*
|
||||
* @param bool $force Erzwingt die Aktualisierung aller Subdomains
|
||||
* @param int $startId Beginnt die Synchronisation ab dieser ID
|
||||
* @param bool $createMissing Erstellt fehlende Subdomains
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return array Ergebnisdaten der Synchronisation
|
||||
*/
|
||||
private function syncAllUsers($force, $startId, $createMissing = false, $debug = false)
|
||||
{
|
||||
$this->info("Starte Synchronisation aller Benutzer-Shops ab ID: {$startId}");
|
||||
|
||||
// Benutzer-Shops abrufen
|
||||
$userShopsQuery = UserShop::query();
|
||||
|
||||
if ($startId > 1) {
|
||||
$userShopsQuery->where('id', '>=', $startId);
|
||||
}
|
||||
|
||||
$userShops = $userShopsQuery->limit(1000)->get();
|
||||
$this->info("Gefundene Benutzer-Shops: " . $userShops->count());
|
||||
|
||||
// Subdomains abrufen und filtern
|
||||
$subdomains = $this->getFilteredSubdomains();
|
||||
$this->info("Gefilterte Subdomains: " . count($subdomains));
|
||||
|
||||
// Ergebnis-Arrays initialisieren
|
||||
$doubleDomains = [];
|
||||
$missingSubdomains = [];
|
||||
$outdatedPhpVersions = [];
|
||||
$missingSSL = [];
|
||||
$sslConfigurationNeeded = [];
|
||||
$createdSubdomains = [];
|
||||
$updatedCount = 0;
|
||||
$sslEnabledCount = 0;
|
||||
$sslConfiguredCount = 0;
|
||||
|
||||
// Benutzer-Shops durchlaufen und mit Subdomains abgleichen
|
||||
foreach ($userShops as $userShop) {
|
||||
$fullDomainName = $userShop->slug . '.' . $this->domain;
|
||||
|
||||
// Status der Subdomain setzen
|
||||
$userShop->hasSubdomain = false;
|
||||
$userShop->hasSSL = false;
|
||||
$userShop->sslActive = false;
|
||||
$userShop->PHPversion = "";
|
||||
|
||||
// Prüfen, ob Subdomain existiert
|
||||
if (array_key_exists($fullDomainName, $subdomains)) {
|
||||
$userShop->hasSubdomain = true;
|
||||
$userShop->hasSSL = ($subdomains[$fullDomainName]['ssl_certificate_sni'] === 'Y' ||
|
||||
$subdomains[$fullDomainName]['ssl_proxy'] === 'Y');
|
||||
$userShop->sslActive = ($subdomains[$fullDomainName]['ssl_certificate_sni_is_active'] ?? 'N') === 'Y';
|
||||
$userShop->PHPversion = $subdomains[$fullDomainName]['php_version'];
|
||||
|
||||
// Prüfen, ob PHP-Version aktualisiert werden muss
|
||||
$requiredPhpVersion = config('app.php_version');
|
||||
if ($force || $userShop->PHPversion !== $requiredPhpVersion) {
|
||||
$this->info("Shop {$userShop->slug}: PHP-Version aktualisieren von {$userShop->PHPversion} auf {$requiredPhpVersion}" . ($debug ? " (simuliert)" : ""));
|
||||
$outdatedPhpVersions[] = $userShop->slug;
|
||||
|
||||
if (!$debug && $this->updateSubdomainParams($userShop->slug, $requiredPhpVersion)) {
|
||||
$updatedCount++;
|
||||
} else if ($debug) {
|
||||
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
|
||||
$updatedCount++;
|
||||
}
|
||||
}else{
|
||||
$this->info("Shop {$userShop->slug}: PHP-Version ist aktuell: {$userShop->PHPversion}");
|
||||
}
|
||||
|
||||
// Prüfen, ob SSL aktiviert werden muss
|
||||
/* if ($force || !$userShop->hasSSL) {
|
||||
$this->info("Shop {$userShop->slug}: SSL aktivieren" . ($debug ? " (simuliert)" : ""));
|
||||
$missingSSL[] = $userShop->slug;
|
||||
|
||||
if (!$debug && $this->enableSSL($userShop->slug)) {
|
||||
$sslEnabledCount++;
|
||||
} else if ($debug) {
|
||||
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
|
||||
$sslEnabledCount++;
|
||||
}
|
||||
}
|
||||
// Prüfen, ob SSL-Konfiguration aktualisiert werden muss
|
||||
else if ($force || ($userShop->hasSSL && !$userShop->sslActive)) {
|
||||
$this->info("Shop {$userShop->slug}: SSL-Konfiguration aktualisieren" . ($debug ? " (simuliert)" : ""));
|
||||
$sslConfigurationNeeded[] = $userShop->slug;
|
||||
|
||||
if (!$debug && $this->updateSSL($fullDomainName)) {
|
||||
$sslConfiguredCount++;
|
||||
} else if ($debug) {
|
||||
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
|
||||
$sslConfiguredCount++;
|
||||
}
|
||||
}*/
|
||||
|
||||
// Subdomain aus der Liste entfernen, um später ungenutzte zu identifizieren
|
||||
unset($subdomains[$fullDomainName]);
|
||||
} else {
|
||||
// Subdomain fehlt
|
||||
$missingSubdomains[] = $userShop->slug;
|
||||
$this->warn("Shop {$userShop->slug}: Keine Subdomain gefunden");
|
||||
|
||||
// Optional: Neue Subdomain erstellen
|
||||
if ($createMissing) {
|
||||
$this->info("Erstelle fehlende Subdomain für Shop {$userShop->slug}" . ($debug ? " (simuliert)" : ""));
|
||||
if (!$debug && $this->createSubdomain($userShop->slug)) {
|
||||
$createdSubdomains[] = $userShop->slug;
|
||||
} else if ($debug) {
|
||||
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
|
||||
$createdSubdomains[] = $userShop->slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Doppelte Domains pro Benutzer erfassen
|
||||
$doubleDomains[$userShop->user_id][$userShop->id] = $fullDomainName;
|
||||
}
|
||||
|
||||
// Bereinigen der doppelten Domains (nur Benutzer mit mehreren Shops behalten)
|
||||
foreach ($doubleDomains as $userId => $domains) {
|
||||
if (count($domains) === 1) {
|
||||
unset($doubleDomains[$userId]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logExecutionTime("Synchronisation abgeschlossen" . ($debug ? " (DEBUG-MODUS)" : ""));
|
||||
|
||||
// Ergebnisdaten zurückgeben
|
||||
return [
|
||||
'shops' => $userShops,
|
||||
'unusedSubdomains' => $subdomains,
|
||||
'doubleDomains' => $doubleDomains,
|
||||
'missingSubdomains' => $missingSubdomains,
|
||||
'outdatedPhpVersions' => $outdatedPhpVersions,
|
||||
'missingSSL' => $missingSSL,
|
||||
'sslConfigurationNeeded' => $sslConfigurationNeeded,
|
||||
'createdSubdomains' => $createdSubdomains,
|
||||
'updatedCount' => $updatedCount,
|
||||
'sslEnabledCount' => $sslEnabledCount,
|
||||
'sslConfiguredCount' => $sslConfiguredCount
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft alle Subdomains ab und filtert sie
|
||||
*
|
||||
* @return array Gefilterte Subdomains
|
||||
*/
|
||||
private function getFilteredSubdomains()
|
||||
{
|
||||
$kas = new KasController();
|
||||
$subdomains = [];
|
||||
|
||||
// Alle Subdomains abrufen
|
||||
$this->info("Rufe Subdomains von KAS ab...");
|
||||
$getSubdomains = $kas->action('get_subdomains');
|
||||
|
||||
// Subdomains filtern und in ein leicht zugängliches Array umwandeln
|
||||
foreach ($getSubdomains as $subdomain) {
|
||||
if (!isset($subdomain['subdomain_name'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Spezielle Subdomains überspringen
|
||||
$skip = false;
|
||||
foreach ($this->skipPrefixes as $prefix) {
|
||||
if (strpos($subdomain['subdomain_name'], $prefix) !== false) {
|
||||
$skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Subdomain-Informationen speichern
|
||||
$subdomains[$subdomain['subdomain_name']] = [
|
||||
'ssl_certificate_sni' => $subdomain['ssl_certificate_sni'] ?? 'N',
|
||||
'php_version' => $subdomain['php_version'] ?? '',
|
||||
'ssl_proxy' => $subdomain['ssl_proxy'] ?? 'N',
|
||||
'ssl_certificate_sni_is_active' => $subdomain['ssl_certificate_sni_is_active'] ?? 'N',
|
||||
];
|
||||
}
|
||||
|
||||
return $subdomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ändert Parameter einer Subdomain, insbesondere die PHP-Version
|
||||
*
|
||||
* @param string $subdomain Name der Subdomain ohne Domain
|
||||
* @param string $phpVersion Neue PHP-Version (z.B. '8.2')
|
||||
* @param array $additionalParams Weitere zu ändernde Parameter
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return bool Erfolg der Operation
|
||||
*/
|
||||
private function updateSubdomainParams($subdomain, $phpVersion, $additionalParams = [], $debug = false)
|
||||
{
|
||||
$this->info("Aktualisiere Parameter für: {$subdomain}.{$this->domain}" . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if ($debug) {
|
||||
$this->line(" - PHP-Version: {$phpVersion}");
|
||||
if (!empty($additionalParams)) {
|
||||
$this->line(" - Zusätzliche Parameter: " . json_encode($additionalParams));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$kas = new KasController();
|
||||
|
||||
// Standardparameter
|
||||
$params = [
|
||||
'subdomain_name' => $subdomain . '.' . $this->domain, // Vollständigen Domainnamen verwenden
|
||||
'php_version' => $phpVersion
|
||||
];
|
||||
|
||||
// Oder alternativ, falls die API die Subdomain und Domain getrennt erwartet:
|
||||
// $params = [
|
||||
// 'subdomain_name' => $subdomain,
|
||||
// 'domain_name' => $this->domain,
|
||||
// 'php_version' => $phpVersion
|
||||
// ];
|
||||
|
||||
// Zusätzliche Parameter hinzufügen
|
||||
$params = array_merge($params, $additionalParams);
|
||||
|
||||
// Subdomain aktualisieren
|
||||
$result = $kas->action('update_subdomain', $params);
|
||||
$this->info("Parameter: ".json_encode($params));
|
||||
|
||||
if ($result) {
|
||||
$this->info("Parameter für {$subdomain}.{$this->domain} erfolgreich aktualisiert " . json_encode($result));
|
||||
return true;
|
||||
} else {
|
||||
$this->error("Fehler beim Aktualisieren der Parameter für {$subdomain}.{$this->domain}: " . json_encode($result));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->error("Fehler beim Aktualisieren der Parameter für {$subdomain}.{$this->domain}: " . $e->getMessage());
|
||||
Log::error("Subdomain Parameter Update Fehler", [
|
||||
'subdomain' => $subdomain,
|
||||
'domain' => $this->domain,
|
||||
'php_version' => $phpVersion,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert die SSL-Konfiguration einer Subdomain mit erweiterten Parametern
|
||||
*
|
||||
* @param string $subdomainName Vollständiger Domainname (z.B. 'shop.mivita.care')
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return bool Erfolg der Operation
|
||||
*/
|
||||
private function updateSSL($subdomainName, $debug = false)
|
||||
{
|
||||
$this->info("Aktualisiere SSL-Konfiguration für: {$subdomainName}" . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if ($debug) {
|
||||
$this->line(" - SSL-Parameter werden aktualisiert");
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$kas = new KasController();
|
||||
$ssl = KasSLLController::getApiSSLParameter();
|
||||
|
||||
$params = array_merge(['hostname' => $subdomainName], $ssl);
|
||||
$result = $kas->action('update_ssl', $params);
|
||||
|
||||
if ($result === "TRUE" || $result === true) {
|
||||
$this->info("SSL-Konfiguration für {$subdomainName} erfolgreich aktualisiert");
|
||||
return true;
|
||||
} else {
|
||||
$this->warn("SSL-Konfiguration für {$subdomainName} nicht vollständig aktualisiert: " . json_encode($result));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->error("Fehler bei der SSL-Konfiguration für {$subdomainName}: " . $e->getMessage());
|
||||
Log::error("SSL Update Fehler", [
|
||||
'subdomain' => $subdomainName,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktiviert SSL für eine Subdomain mit vollständiger Konfiguration
|
||||
*
|
||||
* @param string $subdomain Name der Subdomain ohne Domain
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return bool Erfolg der Operation
|
||||
*/
|
||||
private function enableSSL($subdomain, $debug = false)
|
||||
{
|
||||
$fullDomainName = $subdomain . '.' . $this->domain;
|
||||
$this->info("Aktiviere SSL für: {$fullDomainName}" . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if ($debug) {
|
||||
$this->line(" - SSL-Proxy wird aktiviert");
|
||||
$this->line(" - HTTP zu HTTPS Weiterleitung wird eingerichtet");
|
||||
$this->line(" - SSL-Konfiguration wird aktualisiert");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Schritt 1: Subdomain-Parameter aktualisieren (ssl_proxy aktivieren)
|
||||
$subdomainResult = $this->updateSubdomainParams($subdomain, config('app.php_version'), [
|
||||
'ssl_proxy' => 'Y',
|
||||
'redirect_status' => 301 // Weiterleitung von HTTP auf HTTPS
|
||||
]);
|
||||
|
||||
if (!$subdomainResult) {
|
||||
$this->error("SSL-Aktivierung für {$fullDomainName} fehlgeschlagen: Subdomain-Parameter konnten nicht aktualisiert werden");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Schritt 2: SSL-Konfiguration aktualisieren
|
||||
$sslResult = $this->updateSSL($fullDomainName);
|
||||
|
||||
if (!$sslResult) {
|
||||
$this->warn("SSL-Aktivierung für {$fullDomainName} teilweise erfolgreich: SSL-Konfiguration konnte nicht aktualisiert werden");
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->info("SSL für {$fullDomainName} vollständig aktiviert");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Subdomain für einen Shop
|
||||
*
|
||||
* @param string $slug Shop-Slug
|
||||
* @param bool $debug Debug-Modus (Dry-Run)
|
||||
* @return bool Erfolg der Operation
|
||||
*/
|
||||
private function createSubdomain($slug, $debug = false)
|
||||
{
|
||||
$fullDomainName = $slug . '.' . $this->domain;
|
||||
$this->info("Erstelle neue Subdomain: {$fullDomainName}" . ($debug ? " (simuliert)" : ""));
|
||||
|
||||
if ($debug) {
|
||||
$this->line(" - Pfad: /mein.mivita.care/public/");
|
||||
$this->line(" - PHP-Version: " . config('app.php_version'));
|
||||
$this->line(" - SSL wird direkt aktiviert");
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$kas = new KasController();
|
||||
|
||||
// Parameter für die neue Subdomain
|
||||
$params = [
|
||||
'subdomain_name' => $slug,
|
||||
'domain_name' => $this->domain,
|
||||
'subdomain_path' => '/mein.mivita.care/public/',
|
||||
'php_version' => config('app.php_version'),
|
||||
];
|
||||
|
||||
// Subdomain erstellen
|
||||
$result = $kas->action('add_subdomain', $params);
|
||||
|
||||
if ($result === $fullDomainName) {
|
||||
$this->info("Subdomain {$fullDomainName} erfolgreich erstellt");
|
||||
|
||||
// SSL direkt aktivieren
|
||||
$this->enableSSL($slug);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$this->error("Fehler beim Erstellen der Subdomain {$fullDomainName}: " . json_encode($result));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->error("Fehler beim Erstellen der Subdomain {$fullDomainName}: " . $e->getMessage());
|
||||
Log::error("Subdomain Erstellung Fehler", [
|
||||
'slug' => $slug,
|
||||
'domain' => $this->domain,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Protokolliert die Ausführungszeit einer Operation
|
||||
*
|
||||
* @param string $message Nachricht für die Protokollierung
|
||||
* @return void
|
||||
*/
|
||||
private function logExecutionTime($message)
|
||||
{
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info($message. ' | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\User;
|
||||
use App\Services\ShoppingUserService;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncShoppingUserData extends Command
|
||||
{
|
||||
//aufruf: php artisan shopping:sync-user-data --force --start=4
|
||||
protected $signature = 'shopping:sync-user-data {user_id?} {--force} {--start=1}';
|
||||
protected $description = 'Synchronisiere Shopping User Daten für einen oder alle User';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$userId = $this->argument('user_id');
|
||||
$force = $this->option('force');
|
||||
$startId = $this->option('start');
|
||||
|
||||
try {
|
||||
if ($userId) {
|
||||
$this->syncSingleUser($userId);
|
||||
} else {
|
||||
$this->syncAllUsers($force, $startId);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->error("Ein Fehler ist aufgetreten: " . $e->getMessage());
|
||||
Log::error("Shopping User Sync Fehler: ", [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function syncAllUsers($force, $startId)
|
||||
{
|
||||
$this->info("Starte Synchronisierung für alle User ab ID: {$startId}...");
|
||||
$count = 0;
|
||||
$errors = [];
|
||||
|
||||
// Aktiviere SQL Query Logging für Debugging
|
||||
DB::enableQueryLog();
|
||||
|
||||
User::where('id', '>=', $startId)
|
||||
->orderBy('id')
|
||||
->chunk(10, function($users) use (&$count, &$errors, $force) {
|
||||
foreach($users as $user) {
|
||||
try {
|
||||
$this->info("\nVerarbeite User ID: {$user->id} ({$user->email})");
|
||||
|
||||
$this->syncUser($user);
|
||||
$count++;
|
||||
|
||||
$this->info("✓ User ID {$user->id} erfolgreich synchronisiert");
|
||||
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = "Fehler bei User ID {$user->id} ({$user->email}): " . $e->getMessage();
|
||||
$errors[] = $errorMessage;
|
||||
$this->error($errorMessage);
|
||||
|
||||
// Log die letzten SQL Queries
|
||||
Log::error("Letzte SQL Queries:", [
|
||||
'queries' => DB::getQueryLog()
|
||||
]);
|
||||
|
||||
if (!$force) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info("Synchronisierung abgeschlossen!");
|
||||
$this->info("Gesamt synchronisierte User: {$count}");
|
||||
|
||||
if (count($errors) > 0) {
|
||||
$this->warn("Es gab " . count($errors) . " Fehler während der Synchronisierung:");
|
||||
foreach($errors as $error) {
|
||||
$this->warn("- " . $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function syncUser(User $user)
|
||||
{
|
||||
try {
|
||||
$this->output->write(" Setze Faker Mail... ");
|
||||
ShoppingUserService::setFakerMail($user);
|
||||
$this->info("✓");
|
||||
|
||||
$this->output->write(" Synchronisiere Numbers... ");
|
||||
ShoppingUserService::syncNumbersByEmail($user);
|
||||
$this->info("✓");
|
||||
|
||||
$this->output->write(" Synchronisiere Orders... ");
|
||||
ShoppingUserService::syncOrdersByEmail($user);
|
||||
$this->info("✓");
|
||||
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
private function syncSingleUser($userId)
|
||||
{
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
throw new Exception("User ID {$userId} nicht gefunden");
|
||||
}
|
||||
|
||||
$this->info("Starte Synchronisierung für User ID {$userId}...");
|
||||
|
||||
try {
|
||||
$this->syncUser($user);
|
||||
$this->info("✓ Synchronisierung erfolgreich abgeschlossen");
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Fehler bei User ID {$userId}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Services\BusinessPlan\TreeCalcBotOptimized;
|
||||
use App\Services\BusinessPlan\BusinessUserItemOptimized;
|
||||
use App\Services\BusinessPlan\GrowthBonusCalculator;
|
||||
use App\User;
|
||||
use App\Models\UserBusiness;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class TestGrowthBonusCalculation extends Command
|
||||
{
|
||||
protected $signature = 'test:growth-bonus
|
||||
{user_id : Die User-ID für den Test}
|
||||
{--month=12 : Der Monat}
|
||||
{--year=2025 : Das Jahr}
|
||||
{--debug : Zeigt detaillierte Debug-Informationen}
|
||||
{--from-db : Verwendet gespeicherte Daten und simuliert Berechnung}';
|
||||
|
||||
protected $description = 'Testet die Growth Bonus Berechnung für einen User';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$userId = $this->argument('user_id');
|
||||
$month = (int) $this->option('month');
|
||||
$year = (int) $this->option('year');
|
||||
$debug = $this->option('debug');
|
||||
$fromDb = $this->option('from-db');
|
||||
|
||||
$this->info("=== Growth Bonus Test ===");
|
||||
$this->info("User: {$userId}, Monat: {$month}/{$year}");
|
||||
$this->newLine();
|
||||
|
||||
// User laden
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
$this->error("User {$userId} nicht gefunden!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$date = Carbon::createFromDate($year, $month, 1);
|
||||
|
||||
// Gespeicherte UserBusiness-Daten laden
|
||||
$userBusiness = UserBusiness::where('user_id', $userId)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->first();
|
||||
|
||||
if ($userBusiness) {
|
||||
$this->info("=== GESPEICHERTE DATEN (UserBusiness) ===");
|
||||
$qualLevel = $userBusiness->qual_user_level;
|
||||
$this->table(
|
||||
['Feld', 'Wert'],
|
||||
[
|
||||
['user_id', $userBusiness->user_id],
|
||||
['qual_user_level (Name)', $qualLevel['name'] ?? 'NULL'],
|
||||
['Growth Bonus (aus qual_user_level)', $qualLevel['growth_bonus'] ?? 'NULL'],
|
||||
['active_growth_bonus (gespeichert)', $userBusiness->active_growth_bonus ?? 'NULL'],
|
||||
['commission_growth_total', $userBusiness->commission_growth_total ?? 0],
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$this->warn("Keine gespeicherten UserBusiness-Daten für {$month}/{$year}");
|
||||
if (!$fromDb) {
|
||||
$this->info("Versuche Live-Berechnung...");
|
||||
} else {
|
||||
$this->error("--from-db erfordert gespeicherte Daten!");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fromDb && $userBusiness) {
|
||||
$this->testFromDatabase($userId, $month, $year, $debug);
|
||||
} else {
|
||||
$this->testLiveCalculation($user, $date, $month, $year, $debug);
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info("=== TEST ABGESCHLOSSEN ===");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function testFromDatabase(int $userId, int $month, int $year, bool $debug): void
|
||||
{
|
||||
$this->newLine();
|
||||
$this->info("=== ANALYSE AUS DATENBANK ===");
|
||||
|
||||
// Lade User und seine Firstlines
|
||||
$userBusiness = UserBusiness::where('user_id', $userId)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->first();
|
||||
|
||||
$qualLevel = $userBusiness->qual_user_level;
|
||||
$myGrowthBonus = (float) ($qualLevel['growth_bonus'] ?? 0);
|
||||
|
||||
$this->info("Mein Growth Bonus Anspruch: {$myGrowthBonus}%");
|
||||
|
||||
if ($myGrowthBonus <= 0) {
|
||||
$this->warn("Kein Growth Bonus Anspruch!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Lade Firstlines
|
||||
$firstlineIds = User::where('m_sponsor', $userId)->pluck('id')->toArray();
|
||||
$this->info("Anzahl Firstlines: " . count($firstlineIds));
|
||||
|
||||
$this->newLine();
|
||||
$this->info("=== FIRSTLINE ANALYSE ===");
|
||||
|
||||
$totalExpectedGrowth = 0;
|
||||
$problemsFound = false;
|
||||
|
||||
foreach ($firstlineIds as $flId) {
|
||||
$flBusiness = UserBusiness::where('user_id', $flId)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->first();
|
||||
|
||||
if (!$flBusiness) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$flQualLevel = $flBusiness->qual_user_level;
|
||||
$flGrowthBonus = (float) ($flQualLevel['growth_bonus'] ?? 0);
|
||||
$flLevelName = $flQualLevel['name'] ?? 'Kein Level';
|
||||
$flTPSum = (float) ($flBusiness->sales_volume_points_TP_sum ?? 0);
|
||||
|
||||
if ($flTPSum <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Berechne erwartete Differenz
|
||||
$expectedDiff = max(0, $myGrowthBonus - $flGrowthBonus);
|
||||
$expectedCommission = round($flTPSum / 100 * $expectedDiff, 2);
|
||||
|
||||
// Problem-Erkennung: Wenn FL einen Growth Bonus hat aber wir
|
||||
// trotzdem den vollen Betrag bekommen
|
||||
$isPotentialProblem = $flGrowthBonus > 0 && $expectedDiff < $myGrowthBonus;
|
||||
|
||||
$this->info("--- Firstline User {$flId} ---");
|
||||
$this->table(
|
||||
['Feld', 'Wert'],
|
||||
[
|
||||
['Level erreicht', $flLevelName],
|
||||
['Growth Bonus (FL)', $flGrowthBonus . '%'],
|
||||
['Team-Punkte (TP_sum)', number_format($flTPSum, 0, ',', '.')],
|
||||
['Mein Anspruch', $myGrowthBonus . '%'],
|
||||
['Differenz (erwartet)', $expectedDiff . '%'],
|
||||
['Erwartete Provision', number_format($expectedCommission, 2, ',', '.') . ' €'],
|
||||
['ACHTUNG: Blockade?', $isPotentialProblem ? 'JA - FL sollte blockieren!' : 'Nein'],
|
||||
]
|
||||
);
|
||||
|
||||
if ($isPotentialProblem) {
|
||||
$problemsFound = true;
|
||||
$this->error(" ⚠️ User {$flId} hat {$flLevelName} ({$flGrowthBonus}%) erreicht!");
|
||||
$this->error(" Differenz sollte nur {$expectedDiff}% sein, nicht {$myGrowthBonus}%!");
|
||||
}
|
||||
|
||||
$totalExpectedGrowth += $expectedCommission;
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info("=== ZUSAMMENFASSUNG ===");
|
||||
$this->info("Erwartete Growth Bonus Summe (mit korrekter Differenz): " . number_format($totalExpectedGrowth, 2, ',', '.') . ' €');
|
||||
$this->info("Gespeicherte Growth Bonus Summe: " . number_format($userBusiness->commission_growth_total ?? 0, 2, ',', '.') . ' €');
|
||||
|
||||
if ($problemsFound) {
|
||||
$this->newLine();
|
||||
$this->error("⚠️ PROBLEME GEFUNDEN! Die Blockade durch qualifizierte Firstlines funktioniert möglicherweise nicht korrekt.");
|
||||
}
|
||||
|
||||
// Detailanalyse: Wie wurde die Berechnung durchgeführt?
|
||||
if ($debug) {
|
||||
$this->newLine();
|
||||
$this->info("=== DEBUG: Rekursive Volumen-Analyse ===");
|
||||
$this->analyzeVolumeDistribution($userId, $month, $year, $myGrowthBonus, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private function analyzeVolumeDistribution(int $userId, int $month, int $year, float $myPercent, int $depth = 1): array
|
||||
{
|
||||
if ($depth > 10) {
|
||||
return ['0.0' => 0];
|
||||
}
|
||||
|
||||
$userBusiness = UserBusiness::where('user_id', $userId)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->first();
|
||||
|
||||
if (!$userBusiness) {
|
||||
return ['0.0' => 0];
|
||||
}
|
||||
|
||||
$qualLevel = $userBusiness->qual_user_level;
|
||||
$myProtection = (float) ($qualLevel['growth_bonus'] ?? 0);
|
||||
|
||||
$indent = str_repeat(" ", $depth);
|
||||
|
||||
// Eigenes Volumen (TP_sum - aber nur direkte Punkte, nicht Team)
|
||||
// Bei gespeicherten Daten ist das schwer zu unterscheiden
|
||||
// Vereinfachung: Für den Test nehmen wir TP_sum als Gesamt-Volumen
|
||||
|
||||
$this->line("{$indent}User {$userId}: Protection={$myProtection}%");
|
||||
|
||||
// Lade Kinder
|
||||
$childIds = User::where('m_sponsor', $userId)->pluck('id')->toArray();
|
||||
|
||||
$volumes = [];
|
||||
|
||||
foreach ($childIds as $childId) {
|
||||
$childBusiness = UserBusiness::where('user_id', $childId)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->first();
|
||||
|
||||
if (!$childBusiness) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childTP = (float) ($childBusiness->sales_volume_points_TP_sum ?? 0);
|
||||
if ($childTP <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childQual = $childBusiness->qual_user_level;
|
||||
$childProtection = (float) ($childQual['growth_bonus'] ?? 0);
|
||||
$childLevelName = $childQual['name'] ?? 'Kein Level';
|
||||
|
||||
// Berechne Differenz
|
||||
$diff = max(0, $myPercent - $childProtection);
|
||||
$commission = round($childTP / 100 * $diff, 2);
|
||||
|
||||
$this->line("{$indent} └─ Child {$childId}: {$childLevelName}, Protection={$childProtection}%, TP={$childTP}");
|
||||
$this->line("{$indent} Differenz: {$myPercent}% - {$childProtection}% = {$diff}%");
|
||||
$this->line("{$indent} Provision: {$childTP} * {$diff}% = {$commission}€");
|
||||
|
||||
if ($childProtection > 0) {
|
||||
$this->warn("{$indent} ⚠️ BLOCKADE durch {$childLevelName}!");
|
||||
}
|
||||
}
|
||||
|
||||
return $volumes;
|
||||
}
|
||||
|
||||
private function testLiveCalculation(User $user, Carbon $date, int $month, int $year, bool $debug): void
|
||||
{
|
||||
$this->newLine();
|
||||
$this->info("=== LIVE-BERECHNUNG ===");
|
||||
|
||||
// TreeCalcBot erstellen
|
||||
$treeCalcBot = new TreeCalcBotOptimized($month, $year, 'member', true);
|
||||
|
||||
// BusinessUserItem erstellen
|
||||
$businessUserItem = new BusinessUserItemOptimized($date, $treeCalcBot);
|
||||
$businessUserItem->makeUserFromModel($user, true);
|
||||
$businessUserItem->addUserID();
|
||||
|
||||
// Kinder laden
|
||||
$businessUserItem->readParentsBusinessUsers(true, 0);
|
||||
|
||||
// Qualifikation berechnen
|
||||
$businessUserItem->calcQualPP(true);
|
||||
|
||||
$qualUserLevel = $businessUserItem->getQualUserLevel();
|
||||
|
||||
$this->table(
|
||||
['Feld', 'Wert'],
|
||||
[
|
||||
['user_id', $businessUserItem->user_id],
|
||||
['isQualLevel()', $businessUserItem->isQualLevel() ? 'JA' : 'NEIN'],
|
||||
['isQualificationCalculated()', $businessUserItem->isQualificationCalculated() ? 'JA' : 'NEIN'],
|
||||
['getQualUserLevel() (Name)', $qualUserLevel['name'] ?? 'NULL'],
|
||||
['Growth Bonus (qual_user_level)', $qualUserLevel['growth_bonus'] ?? 'NULL'],
|
||||
['getActiveGrowthBonus()', $businessUserItem->getActiveGrowthBonus()],
|
||||
['getQualifiedGrowthBonus()', $businessUserItem->getQualifiedGrowthBonus()],
|
||||
]
|
||||
);
|
||||
|
||||
$this->newLine();
|
||||
$this->info("=== FIRSTLINES (Kinder) ===");
|
||||
|
||||
if (empty($businessUserItem->businessUserItems)) {
|
||||
$this->warn("Keine Firstlines geladen!");
|
||||
} else {
|
||||
foreach ($businessUserItem->businessUserItems as $index => $childItem) {
|
||||
$childQual = $childItem->getQualUserLevel();
|
||||
|
||||
$this->info("--- Firstline " . ($index + 1) . " ---");
|
||||
$this->table(
|
||||
['Feld', 'Wert'],
|
||||
[
|
||||
['user_id', $childItem->user_id],
|
||||
['isQualLevel()', $childItem->isQualLevel() ? 'JA' : 'NEIN'],
|
||||
['getQualUserLevel() (Name)', $childQual['name'] ?? 'NULL'],
|
||||
['Growth Bonus (qual_user_level)', $childQual['growth_bonus'] ?? 'NULL'],
|
||||
['getActiveGrowthBonus()', $childItem->getActiveGrowthBonus()],
|
||||
['getQualifiedGrowthBonus()', $childItem->getQualifiedGrowthBonus()],
|
||||
['sales_volume_points_TP_sum', $childItem->sales_volume_points_TP_sum ?? 0],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($qualUserLevel && ($qualUserLevel['growth_bonus'] ?? 0) > 0) {
|
||||
$calculator = new GrowthBonusCalculator();
|
||||
$qualData = (object) $qualUserLevel;
|
||||
$totalGrowthBonus = $calculator->calculate($businessUserItem, $qualData);
|
||||
|
||||
$this->newLine();
|
||||
$this->info("Berechneter Growth Bonus: {$totalGrowthBonus}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,321 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\User;
|
||||
use App\Models\UserBusiness;
|
||||
use App\Models\UserLevel;
|
||||
use App\Cron\UserLevelUpdate;
|
||||
use App\Console\Commands\BusinessStoreOptimized;
|
||||
use Illuminate\Console\Command;
|
||||
use ReflectionClass;
|
||||
|
||||
class TestUserLevelUpdate extends Command
|
||||
{
|
||||
/**
|
||||
* php artisan test:user-level-update {month} {year} {--user_id=} {--send-mail} {--dry-run}
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'test:user-level-update {month} {year} {--user_id= : Test für spezifischen User} {--send-mail : E-Mail senden} {--dry-run : Nur anzeigen, nicht speichern}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Testet die UserLevelUpdate-Funktion aus BusinessStoreOptimized für einen Monat/Jahr oder spezifischen User';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$month = (int) $this->argument('month');
|
||||
$year = (int) $this->argument('year');
|
||||
$userId = $this->option('user_id') ? (int) $this->option('user_id') : null;
|
||||
$sendMail = $this->option('send-mail');
|
||||
$dryRun = $this->option('dry-run');
|
||||
|
||||
$this->info("===========================================");
|
||||
$this->info("UserLevelUpdate Test");
|
||||
$this->info("===========================================");
|
||||
$this->line("Monat: {$month}");
|
||||
$this->line("Jahr: {$year}");
|
||||
if ($userId) {
|
||||
$this->line("User ID: {$userId}");
|
||||
}
|
||||
$this->line("E-Mail senden: " . ($sendMail ? 'Ja' : 'Nein'));
|
||||
$this->line("Dry-Run (nur zeigen): " . ($dryRun ? 'Ja' : 'Nein'));
|
||||
$this->line("");
|
||||
|
||||
if ($dryRun) {
|
||||
// Im Dry-Run Modus zeigen wir nur die Analyse
|
||||
$this->performDryRunAnalysis($month, $year, $userId);
|
||||
} else {
|
||||
// Nutze die originale Funktion aus BusinessStoreOptimized
|
||||
$this->runOriginalFunction($month, $year, $sendMail);
|
||||
}
|
||||
|
||||
$this->info("");
|
||||
$this->info("===========================================");
|
||||
$this->info("Test abgeschlossen!");
|
||||
$this->info("===========================================");
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Test fehlgeschlagen: ' . $e->getMessage());
|
||||
$this->error('Stack trace: ' . $e->getTraceAsString());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt die originale userLevelUpdate Funktion aus BusinessStoreOptimized aus
|
||||
*/
|
||||
private function runOriginalFunction(int $month, int $year, bool $sendMail)
|
||||
{
|
||||
$this->info("Erstelle BusinessStoreOptimized Instanz...");
|
||||
|
||||
// Erstelle BusinessStoreOptimized Command-Instanz
|
||||
$businessStoreCommand = new BusinessStoreOptimized();
|
||||
|
||||
// Setze Output auf aktuellen Command (damit Ausgaben weitergeleitet werden)
|
||||
$businessStoreCommand->setOutput($this->output);
|
||||
|
||||
// Setze Monat und Jahr über Reflection (da private)
|
||||
$reflection = new ReflectionClass($businessStoreCommand);
|
||||
|
||||
$monthProperty = $reflection->getProperty('month');
|
||||
$monthProperty->setAccessible(true);
|
||||
$monthProperty->setValue($businessStoreCommand, $month);
|
||||
|
||||
$yearProperty = $reflection->getProperty('year');
|
||||
$yearProperty->setAccessible(true);
|
||||
$yearProperty->setValue($businessStoreCommand, $year);
|
||||
|
||||
$timeStartProperty = $reflection->getProperty('timeStart');
|
||||
$timeStartProperty->setAccessible(true);
|
||||
$timeStartProperty->setValue($businessStoreCommand, microtime(true));
|
||||
|
||||
// Setze sendUpdateMail
|
||||
$businessStoreCommand->setSendUpdateMail($sendMail);
|
||||
|
||||
$this->info("Führe originale userLevelUpdate() Funktion aus...");
|
||||
$this->line("");
|
||||
|
||||
// Rufe die originale Funktion auf
|
||||
$businessStoreCommand->userLevelUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt Dry-Run Analyse durch
|
||||
*/
|
||||
private function performDryRunAnalysis(int $month, int $year, ?int $userId)
|
||||
{
|
||||
$userLevelUpdate = new UserLevelUpdate($month, $year);
|
||||
|
||||
if ($userId) {
|
||||
// Test für spezifischen User
|
||||
$this->testSingleUserDryRun($userLevelUpdate, $userId);
|
||||
} else {
|
||||
// Test für alle User
|
||||
$this->testAllUsersDryRun($userLevelUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dry-Run Analyse für einen spezifischen User
|
||||
*/
|
||||
private function testSingleUserDryRun(UserLevelUpdate $userLevelUpdate, int $userId)
|
||||
{
|
||||
$userBusiness = UserBusiness::with('user')
|
||||
->where('month', $this->argument('month'))
|
||||
->where('year', $this->argument('year'))
|
||||
->where('user_id', $userId)
|
||||
->whereNotNull('next_qual_user_level')
|
||||
->whereRaw("JSON_LENGTH(next_qual_user_level) > 0")
|
||||
->first();
|
||||
|
||||
if (!$userBusiness) {
|
||||
$this->warn("Keine UserBusiness mit next_qual_user_level gefunden für User ID: {$userId}");
|
||||
|
||||
// Zeige vorhandene UserBusiness-Daten
|
||||
$anyUserBusiness = UserBusiness::where('user_id', $userId)
|
||||
->where('month', $this->argument('month'))
|
||||
->where('year', $this->argument('year'))
|
||||
->first();
|
||||
|
||||
if ($anyUserBusiness) {
|
||||
$this->info("UserBusiness existiert, aber hat kein next_qual_user_level");
|
||||
$this->line("Current Level ID: " . ($anyUserBusiness->m_level_id ?? 'NULL'));
|
||||
$this->line("next_qual_user_level: " . (is_null($anyUserBusiness->next_qual_user_level) ? 'NULL' : json_encode($anyUserBusiness->next_qual_user_level)));
|
||||
} else {
|
||||
$this->warn("Keine UserBusiness gefunden für diesen Monat/Jahr");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$this->displayUserBusinessInfo($userBusiness);
|
||||
$this->info("");
|
||||
$this->info("DRY-RUN MODUS: Änderungen werden nicht gespeichert");
|
||||
$this->analyzeLevelUpdate($userBusiness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dry-Run Analyse für alle User
|
||||
*/
|
||||
private function testAllUsersDryRun(UserLevelUpdate $userLevelUpdate)
|
||||
{
|
||||
$levelUpdateUsers = $userLevelUpdate->getUserBusinessByMonthYear();
|
||||
|
||||
$this->info("Gefunden: " . $levelUpdateUsers->count() . " UserBusiness-Einträge mit next_qual_user_level");
|
||||
$this->line("");
|
||||
|
||||
if ($levelUpdateUsers->count() === 0) {
|
||||
$this->warn("Keine UserBusiness-Einträge mit Level-Updates gefunden.");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("DRY-RUN MODUS: Änderungen werden nicht gespeichert");
|
||||
$this->line("");
|
||||
|
||||
foreach ($levelUpdateUsers as $userBusiness) {
|
||||
$this->line("---------------------------------------------------");
|
||||
$this->displayUserBusinessInfo($userBusiness);
|
||||
$this->analyzeLevelUpdate($userBusiness);
|
||||
$this->line("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt Informationen über UserBusiness
|
||||
*/
|
||||
private function displayUserBusinessInfo(UserBusiness $userBusiness)
|
||||
{
|
||||
$user = $userBusiness->user;
|
||||
|
||||
$this->line("User ID: " . ($user ? $user->id : 'NULL'));
|
||||
$this->line("E-Mail: " . ($user ? $user->email : 'N/A'));
|
||||
$this->line("Aktuelles Level: " . ($user && $user->m_level ? $user->m_level . ' (' . $this->getLevelName($user->m_level) . ')' : 'Kein Level'));
|
||||
$this->line("UserBusiness Level ID: " . ($userBusiness->m_level_id ?? 'NULL'));
|
||||
$this->line("UserBusiness Level Name: " . ($userBusiness->user_level_name ?? 'NULL'));
|
||||
|
||||
$nextQual = $userBusiness->next_qual_user_level;
|
||||
if (is_array($nextQual)) {
|
||||
if (isset($nextQual['id'])) {
|
||||
// Einzelnes Level
|
||||
$this->line("Nächstes qualifiziertes Level: ID " . $nextQual['id'] . ' - ' . ($nextQual['name'] ?? 'N/A') . ' (POS: ' . ($nextQual['pos'] ?? 'N/A') . ')');
|
||||
$this->line(" Bereits aktualisiert: " . (isset($nextQual['hasUpdated']) && $nextQual['hasUpdated'] == 1 ? 'Ja' : 'Nein'));
|
||||
} else {
|
||||
// Array von Leveln
|
||||
$this->line("Nächste qualifizierte Level: " . count($nextQual) . " Level gefunden");
|
||||
foreach ($nextQual as $idx => $level) {
|
||||
if (is_array($level) && isset($level['id'])) {
|
||||
$updated = isset($level['hasUpdated']) && $level['hasUpdated'] == 1 ? ' (bereits aktualisiert)' : '';
|
||||
$this->line(" [{$idx}] ID " . $level['id'] . ' - ' . ($level['name'] ?? 'N/A') . ' (POS: ' . ($level['pos'] ?? 'N/A') . ')' . $updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->line("next_qual_user_level: " . (is_null($nextQual) ? 'NULL' : gettype($nextQual)));
|
||||
}
|
||||
|
||||
$this->line("Total Qual PP: " . ($userBusiness->total_qual_pp ?? 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysiert ob und wie ein Level-Update durchgeführt würde (Dry-Run)
|
||||
*/
|
||||
private function analyzeLevelUpdate(UserBusiness $userBusiness)
|
||||
{
|
||||
$user = $userBusiness->user;
|
||||
if (!$user) {
|
||||
$this->warn("⚠ Kein User-Objekt vorhanden");
|
||||
return;
|
||||
}
|
||||
|
||||
$nextQual = $userBusiness->next_qual_user_level;
|
||||
if (!is_array($nextQual) || empty($nextQual)) {
|
||||
$this->warn("⚠ next_qual_user_level ist kein gültiges Array");
|
||||
return;
|
||||
}
|
||||
|
||||
// Lade UserLevels für Vergleich
|
||||
$userLevels = UserLevel::where('active', 1)->orderBy('pos')->get()->keyBy('id');
|
||||
|
||||
// Prüfe ob einzelnes Level oder Array
|
||||
$levelArray = isset($nextQual['id']) ? [$nextQual] : $nextQual;
|
||||
|
||||
$currentUserLevel = null;
|
||||
if ($user->m_level) {
|
||||
$currentUserLevel = $userLevels->get($user->m_level);
|
||||
}
|
||||
|
||||
$this->info("");
|
||||
$this->info("📊 Analyse:");
|
||||
|
||||
if ($currentUserLevel) {
|
||||
$this->line(" Aktuelles Level POS: {$currentUserLevel->pos}");
|
||||
} else {
|
||||
$this->line(" Aktuelles Level: Kein Level gesetzt");
|
||||
}
|
||||
|
||||
$wouldUpdate = false;
|
||||
$highestLevel = null;
|
||||
$highestPos = 0;
|
||||
|
||||
foreach ($levelArray as $levelData) {
|
||||
if (!is_array($levelData) || !isset($levelData['id'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($levelData['hasUpdated']) && $levelData['hasUpdated'] == 1) {
|
||||
$this->line(" ⏭ Level ID {$levelData['id']} wurde bereits aktualisiert");
|
||||
continue;
|
||||
}
|
||||
|
||||
$newLevel = $userLevels->get($levelData['id']);
|
||||
$newLevelPos = $newLevel ? $newLevel->pos : ($levelData['pos'] ?? 0);
|
||||
|
||||
$levelName = $levelData['name'] ?? 'N/A';
|
||||
$this->line(" 📈 Level ID {$levelData['id']} ({$levelName}): POS {$newLevelPos}");
|
||||
|
||||
if (!$currentUserLevel || $newLevelPos > $currentUserLevel->pos) {
|
||||
if ($newLevelPos > $highestPos) {
|
||||
$highestPos = $newLevelPos;
|
||||
$highestLevel = $levelData;
|
||||
$wouldUpdate = true;
|
||||
}
|
||||
} else {
|
||||
$this->line(" ⚠ Level ist nicht höher als aktuelles Level (POS {$currentUserLevel->pos})");
|
||||
}
|
||||
}
|
||||
|
||||
if ($wouldUpdate && $highestLevel) {
|
||||
$this->info("");
|
||||
$highestLevelName = $highestLevel['name'] ?? 'N/A';
|
||||
$this->info("✅ Würde Level aktualisieren zu: {$highestLevel['id']} ({$highestLevelName})");
|
||||
$this->line(" Von: " . ($currentUserLevel ? "POS {$currentUserLevel->pos}" : "Kein Level") . " → Zu: POS {$highestPos}");
|
||||
} else {
|
||||
$this->info("");
|
||||
$this->warn("⚠ Kein Level-Update würde durchgeführt:");
|
||||
if (!$wouldUpdate) {
|
||||
$this->line(" Kein höheres Level gefunden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt Level-Name nach ID
|
||||
*/
|
||||
private function getLevelName($levelId)
|
||||
{
|
||||
$level = UserLevel::find($levelId);
|
||||
return $level ? $level->name : 'Unbekannt';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,387 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Cron\UserMakeOrder;
|
||||
use App\Models\UserAbo;
|
||||
use App\Models\UserAboOrder;
|
||||
use App\Services\AboHelper;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TestUserMakeAboOrder extends Command
|
||||
{
|
||||
/**
|
||||
* php artisan test:user-make-abo-order {--abo_id=} {--date=} {--dry-run} {--force}
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'test:user-make-abo-order
|
||||
{--abo_id= : Test für spezifisches Abo (ID)}
|
||||
{--date= : Test-Datum im Format Y-m-d (Standard: heute)}
|
||||
{--dry-run : Nur anzeigen, keine Bestellung erstellen}
|
||||
{--force : Überschreibt Duplikatsprüfung}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Testet die Abo-Bestellungserstellung für ein oder mehrere Abos';
|
||||
|
||||
private $timeStart;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->timeStart = microtime(true);
|
||||
$this->info('=== Test: UserMakeAboOrder ===');
|
||||
$this->newLine();
|
||||
|
||||
try {
|
||||
$aboId = $this->option('abo_id');
|
||||
$testDate = $this->option('date') ? Carbon::parse($this->option('date'))->format('Y-m-d') : Carbon::now()->format('Y-m-d');
|
||||
$dryRun = $this->option('dry-run');
|
||||
$force = $this->option('force');
|
||||
|
||||
$this->info("Test-Datum: {$testDate}");
|
||||
if ($dryRun) {
|
||||
$this->warn('DRY-RUN Modus: Es werden keine Bestellungen erstellt!');
|
||||
}
|
||||
if ($force) {
|
||||
$this->warn('FORCE Modus: Duplikatsprüfung wird überschrieben!');
|
||||
}
|
||||
$this->newLine();
|
||||
|
||||
if ($aboId) {
|
||||
// Test für spezifisches Abo
|
||||
$userAbo = UserAbo::find($aboId);
|
||||
if (! $userAbo) {
|
||||
$this->error("Abo mit ID {$aboId} nicht gefunden!");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->testSingleAbo($userAbo, $testDate, $dryRun, $force);
|
||||
} else {
|
||||
// Test für alle fälligen Abos
|
||||
$this->testAllAbos($testDate, $dryRun, $force);
|
||||
}
|
||||
|
||||
$executionTime = $this->getExecutionTime();
|
||||
$this->newLine();
|
||||
$this->info("Test erfolgreich abgeschlossen in {$executionTime}");
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Fehler beim Testen: '.$e->getMessage());
|
||||
$this->error($e->getTraceAsString());
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Testet ein einzelnes Abo
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @param string $testDate
|
||||
* @param bool $dryRun
|
||||
* @param bool $force
|
||||
* @return void
|
||||
*/
|
||||
private function testSingleAbo($userAbo, $testDate, $dryRun, $force)
|
||||
{
|
||||
$this->info("Teste Abo ID: {$userAbo->id}");
|
||||
$this->displayAboInfo($userAbo);
|
||||
|
||||
// Prüfe ob Abo für Test-Datum fällig ist
|
||||
if ($userAbo->next_date != $testDate && ! $force) {
|
||||
$this->warn("Abo ist nicht für {$testDate} fällig (next_date: {$userAbo->next_date})");
|
||||
if (! $this->confirm('Trotzdem fortfahren?', false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Prüfe auf Duplikate
|
||||
if (! $force) {
|
||||
$existingOrder = UserAboOrder::where('user_abo_id', $userAbo->id)
|
||||
->whereDate('created_at', $testDate)
|
||||
->first();
|
||||
|
||||
if ($existingOrder) {
|
||||
$this->warn("Es existiert bereits eine Bestellung für dieses Abo am {$testDate}");
|
||||
$this->info("Bestell-ID: {$existingOrder->shopping_order_id}");
|
||||
if (! $this->confirm('Trotzdem fortfahren?', false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Starte Bestellungserstellung...');
|
||||
|
||||
if ($dryRun) {
|
||||
$this->info('[DRY-RUN] Bestellung würde erstellt werden');
|
||||
$this->displayOrderPreview($userAbo);
|
||||
} else {
|
||||
// Temporär next_date setzen für Test
|
||||
$originalNextDate = $userAbo->next_date;
|
||||
if ($userAbo->next_date != $testDate) {
|
||||
$userAbo->next_date = $testDate;
|
||||
$userAbo->save();
|
||||
$this->info("Temporär next_date auf {$testDate} gesetzt");
|
||||
}
|
||||
|
||||
try {
|
||||
$shoppingOrder = $this->makeOrder($userAbo, $dryRun);
|
||||
|
||||
if ($shoppingOrder) {
|
||||
$this->info("✓ Bestellung erfolgreich erstellt: ID {$shoppingOrder->id}");
|
||||
} else {
|
||||
$this->error('✗ Bestellung konnte nicht erstellt werden');
|
||||
}
|
||||
} finally {
|
||||
// next_date zurücksetzen falls geändert
|
||||
if ($originalNextDate != $testDate) {
|
||||
$userAbo->next_date = $originalNextDate;
|
||||
$userAbo->save();
|
||||
$this->info("next_date zurückgesetzt auf {$originalNextDate}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Testet alle fälligen Abos
|
||||
*
|
||||
* @param string $testDate
|
||||
* @param bool $dryRun
|
||||
* @param bool $force
|
||||
* @return void
|
||||
*/
|
||||
private function testAllAbos($testDate, $dryRun, $force)
|
||||
{
|
||||
$query = UserAbo::where('next_date', '=', $testDate)
|
||||
->where('active', true);
|
||||
|
||||
if (! $force) {
|
||||
$query->whereDoesntHave('user_abo_orders', function ($q) use ($testDate) {
|
||||
$q->whereDate('created_at', $testDate);
|
||||
});
|
||||
}
|
||||
|
||||
$userAbos = $query->get();
|
||||
$count = $userAbos->count();
|
||||
|
||||
$this->info("Gefundene fällige Abos: {$count}");
|
||||
$this->newLine();
|
||||
|
||||
if ($count === 0) {
|
||||
$this->warn('Keine fälligen Abos gefunden!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->confirm("Möchten Sie {$count} Abo(s) testen?", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
|
||||
foreach ($userAbos as $userAbo) {
|
||||
$this->info("--- Abo ID: {$userAbo->id} ---");
|
||||
$this->testSingleAbo($userAbo, $testDate, $dryRun, $force);
|
||||
$this->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt Informationen über ein Abo an
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @return void
|
||||
*/
|
||||
private function displayAboInfo($userAbo)
|
||||
{
|
||||
$this->table(
|
||||
['Feld', 'Wert'],
|
||||
[
|
||||
['ID', $userAbo->id],
|
||||
['User ID', $userAbo->user_id],
|
||||
['Payone UserID', $userAbo->payone_userid],
|
||||
['Aktiv', $userAbo->active ? 'Ja' : 'Nein'],
|
||||
['Status', $userAbo->status.' ('.($userAbo->getStatusType() ?? 'unbekannt').')'],
|
||||
['Intervall', $userAbo->abo_interval],
|
||||
['Next Date', $userAbo->next_date],
|
||||
['Last Date', $userAbo->last_date ?? 'Nie'],
|
||||
['Amount', number_format($userAbo->amount / 100, 2, ',', '.').' €'],
|
||||
['is_for', $userAbo->is_for],
|
||||
['Clearing Type', $userAbo->clearingtype],
|
||||
['Items', $userAbo->user_abo_items->count()],
|
||||
]
|
||||
);
|
||||
|
||||
// Zeige Abo-Items
|
||||
if ($userAbo->user_abo_items->count() > 0) {
|
||||
$this->info('Abo-Items:');
|
||||
$items = [];
|
||||
foreach ($userAbo->user_abo_items as $item) {
|
||||
$items[] = [
|
||||
'Product ID' => $item->product_id,
|
||||
'Qty' => $item->qty,
|
||||
'Comp' => $item->comp ?? '-',
|
||||
'Price' => number_format($item->price / 100, 2, ',', '.').' €',
|
||||
];
|
||||
}
|
||||
$this->table(['Product ID', 'Qty', 'Comp', 'Price'], $items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt eine Vorschau der Bestellung an
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @return void
|
||||
*/
|
||||
private function displayOrderPreview($userAbo)
|
||||
{
|
||||
$this->info('Bestell-Vorschau:');
|
||||
$this->info('- Shopping-User würde erstellt/aktualisiert');
|
||||
$this->info('- Bestellung würde mit folgenden Items erstellt:');
|
||||
|
||||
foreach ($userAbo->user_abo_items as $item) {
|
||||
$product = $item->product;
|
||||
$this->info(" • Product ID {$item->product_id}: {$item->qty}x");
|
||||
}
|
||||
|
||||
$this->info('- Zahlung würde durchgeführt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine Bestellung für ein Abo (vereinfachte Version für Test)
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @param bool $dryRun
|
||||
* @return mixed
|
||||
*/
|
||||
private function makeOrder($userAbo, $dryRun = false)
|
||||
{
|
||||
$this->info('Erstelle Shopping-User...');
|
||||
$userOrder = new UserMakeOrder($userAbo);
|
||||
|
||||
if (! $userOrder->createShoppingUser()) {
|
||||
$this->error('Konnte Shopping-User nicht erstellen');
|
||||
|
||||
return null;
|
||||
}
|
||||
$this->info('✓ Shopping-User erstellt');
|
||||
|
||||
$this->info('Erstelle Bestellung...');
|
||||
$shoppingOrder = $userOrder->makeShoppingOrder();
|
||||
$shoppingOrder->mode = 'test'; // immer im test mode testen
|
||||
$shoppingOrder->save();
|
||||
if (! $shoppingOrder) {
|
||||
$this->error('Konnte Bestellung nicht erstellen');
|
||||
|
||||
return null;
|
||||
}
|
||||
$this->info("✓ Bestellung erstellt: ID {$shoppingOrder->id}");
|
||||
|
||||
if ($dryRun) {
|
||||
$this->info('[DRY-RUN] Zahlung würde durchgeführt');
|
||||
$this->info('[DRY-RUN] Abo würde aktualisiert');
|
||||
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
$this->info('Starte Zahlungsvorgang...');
|
||||
try {
|
||||
$response = $userOrder->makePayment();
|
||||
|
||||
if (is_object($response)) {
|
||||
$response = (array) $response;
|
||||
}
|
||||
|
||||
$this->info('Zahlungsantwort: '.json_encode($response, JSON_PRETTY_PRINT));
|
||||
|
||||
if (! isset($response['status'])) {
|
||||
$this->warn('⚠ Kein Status in Zahlungsantwort');
|
||||
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
if ($response['status'] === 'APPROVED') {
|
||||
$this->info('✓ Zahlung erfolgreich');
|
||||
$this->info('Aktualisiere Abo...');
|
||||
$this->updateAbo($userAbo, $shoppingOrder, 1);
|
||||
$this->info('✓ Abo aktualisiert');
|
||||
} elseif ($response['status'] === 'ERROR') {
|
||||
$this->error('✗ Zahlungsfehler');
|
||||
$this->warn('Abo wird beim nächsten Cron-Lauf erneut versucht');
|
||||
} else {
|
||||
$this->warn("⚠ Zahlungsstatus: {$response['status']}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Fehler bei Zahlung: '.$e->getMessage());
|
||||
}
|
||||
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Abo nach erfolgreicher Bestellung (vereinfachte Version)
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @param mixed $shoppingOrder
|
||||
* @param int $status
|
||||
* @return void
|
||||
*/
|
||||
private function updateAbo($userAbo, $shoppingOrder, $status = 1)
|
||||
{
|
||||
try {
|
||||
DB::transaction(function () use ($userAbo, $shoppingOrder, $status) {
|
||||
$updateData = [
|
||||
'next_date' => AboHelper::setNextDate(now(), $userAbo->abo_interval),
|
||||
'last_date' => now(),
|
||||
];
|
||||
|
||||
if ($status !== 1) {
|
||||
$updateData['status'] = $status;
|
||||
}
|
||||
|
||||
$userAbo->update($updateData);
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => $status,
|
||||
'paid' => true,
|
||||
]);
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Fehler beim Aktualisieren des Abos: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die Ausführungszeit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getExecutionTime()
|
||||
{
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
return $sec.' Sekunden und '.round($micro * 1000, 2).' ms';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\UserUtil;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UserCleanUp extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'user:cleanup';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'User Clean Up inactive for Business Structure and UserDetails';
|
||||
|
||||
private $timeStart;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('RUN Command user:cleanup');
|
||||
\Log::channel('cleanup')->info('COMMAND [user:cleanup] started.');
|
||||
|
||||
$this->timeStart = microtime(true);
|
||||
|
||||
// Schritt 1: User löschen, die länger als 2 Monate inaktiv sind
|
||||
$this->deleteInactiveUsers();
|
||||
|
||||
// Schritt 2: Alle inaktiven User deaktivieren (länger als 2 Wochen inaktiv)
|
||||
// Ihre Downline wird dem nächsten aktiven Berater (Sponsor) zugewiesen
|
||||
$this->cleanUpInActiveUser();
|
||||
|
||||
\Log::channel('cleanup')->info('COMMAND [user:cleanup] finished.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht User, die länger als 2 Monate inaktiv sind (payment_account < -2 month)
|
||||
* - Weist deren Vertriebspartner-Kinder dem nächsten aktiven Sponsor zu
|
||||
* - Überträgt deren Shopping-Kunden zum neuen Sponsor
|
||||
* - Konvertiert den User zu einem Shopping-Kunden
|
||||
* - Löscht den User (soft delete)
|
||||
*/
|
||||
private function deleteInactiveUsers()
|
||||
{
|
||||
$methodStartTime = microtime(true);
|
||||
$this->info('START Command deleteInactiveUsers');
|
||||
$count = 0;
|
||||
|
||||
$date = Carbon::now()->modify('-2 month');
|
||||
$delete_users = User::where('admin', 0)->where('payment_account', '<', $date)->get();
|
||||
|
||||
foreach ($delete_users as $delete_user) {
|
||||
\DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Finde nächsten aktiven Sponsor
|
||||
$active_sponsor = UserUtil::findNextActiveSponsor($delete_user->id);
|
||||
|
||||
if (! $active_sponsor) {
|
||||
\Log::channel('cleanup')->error('deleteInactiveUsers find no active_sponsor by delete_user_id: '.$delete_user->id);
|
||||
\DB::rollBack();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prüfe ob User Account-Daten hat
|
||||
if (! $delete_user->account) {
|
||||
\Log::channel('cleanup')->error('deleteInactiveUsers: User has no account data, skipping user_id: '.$delete_user->id);
|
||||
\DB::rollBack();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Setze alle Vertriebspartner-Kinder zum neuen Sponsor
|
||||
UserUtil::setNewSponsorToChilds($delete_user->id, $active_sponsor->id);
|
||||
|
||||
// Übertrage Shopping-User zum neuen Sponsor
|
||||
UserUtil::setShoppingUserToNewMember($delete_user->id, $active_sponsor->id);
|
||||
|
||||
// Konvertiere User zu Client beim neuen Sponsor
|
||||
UserUtil::setUserToClient($delete_user->id, $active_sponsor->id);
|
||||
|
||||
$data = [
|
||||
'user_id' => $delete_user->id,
|
||||
'email' => $delete_user->email,
|
||||
'm_account' => $delete_user->account->m_account,
|
||||
'm_first_name' => $delete_user->account->m_first_name,
|
||||
'm_last_name' => $delete_user->account->m_last_name,
|
||||
];
|
||||
|
||||
// Lösche User (soft delete)
|
||||
UserUtil::deleteUser($delete_user);
|
||||
|
||||
\DB::commit();
|
||||
$count++;
|
||||
\Log::channel('cleanup')->info('deleteUser: '.json_encode($data));
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollBack();
|
||||
\Log::channel('cleanup')->error('deleteInactiveUsers failed for user_id: '.$delete_user->id.' | Error: '.$e->getMessage());
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$diff = microtime(true) - $methodStartTime;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info('END Command deleteInactiveUsers: '.$count.' | Time: '.$sec.'sec :'.round($micro * 1000, 4).' ms');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deaktiviert User, die länger als 2 Wochen inaktiv sind
|
||||
* - Weist deren Vertriebspartner-Kinder dem nächsten aktiven Sponsor zu
|
||||
* - Deaktiviert den User (behält Account, speichert Sponsor in pre_sponsor)
|
||||
* - Shopping-Kunden werden NICHT übertragen (bleiben beim deaktivierten User)
|
||||
*/
|
||||
private function cleanUpInActiveUser()
|
||||
{
|
||||
$methodStartTime = microtime(true);
|
||||
$this->info('START Command cleanUpInActiveUser');
|
||||
$count = 0;
|
||||
|
||||
// Finde User die länger als 2 Wochen inaktiv sind
|
||||
$date = Carbon::now()->modify('-2 weeks');
|
||||
|
||||
$inactive_users = User::where('active', true)
|
||||
->where('m_sponsor', '!=', null)
|
||||
->where('payment_account', '<', $date)
|
||||
->get();
|
||||
|
||||
foreach ($inactive_users as $inactive_user) {
|
||||
\DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Finde nächsten aktiven Sponsor
|
||||
$active_sponsor = UserUtil::findNextActiveSponsor($inactive_user->m_sponsor);
|
||||
|
||||
if (! $active_sponsor) {
|
||||
\Log::channel('cleanup')->error('cleanUpInActiveUser find no active_sponsor by inactive_user: '.$inactive_user->id);
|
||||
\DB::rollBack();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Setze alle Vertriebspartner-Kinder zum neuen Sponsor
|
||||
UserUtil::setNewSponsorToChilds($inactive_user->id, $active_sponsor->id);
|
||||
|
||||
$data = [
|
||||
'user_id' => $inactive_user->id,
|
||||
'email' => $inactive_user->email,
|
||||
'm_account' => $inactive_user->account ? $inactive_user->account->m_account : '',
|
||||
'm_first_name' => $inactive_user->account ? $inactive_user->account->m_first_name : '',
|
||||
'm_last_name' => $inactive_user->account ? $inactive_user->account->m_last_name : '',
|
||||
];
|
||||
|
||||
// Deaktiviere User (setzt pre_sponsor, entfernt m_sponsor, setzt active=false)
|
||||
UserUtil::deactiveUser($inactive_user);
|
||||
|
||||
\DB::commit();
|
||||
$count++;
|
||||
\Log::channel('cleanup')->info('inactive_user: '.json_encode($data));
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollBack();
|
||||
\Log::channel('cleanup')->error('cleanUpInActiveUser failed for user_id: '.$inactive_user->id.' | Error: '.$e->getMessage());
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$diff = microtime(true) - $methodStartTime;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info('END Command cleanUpInActiveUser: '.$count.' | Time: '.$sec.'sec :'.round($micro * 1000, 4).' ms');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,442 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Cron\UserMakeOrder;
|
||||
use App\Models\UserAbo;
|
||||
use App\Models\UserAboOrder;
|
||||
use App\Services\AboHelper;
|
||||
use App\Services\Incentive\IncentiveTracker;
|
||||
use App\Services\MyLog;
|
||||
use App\Services\Payment;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UserMakeAboOrder extends Command
|
||||
{
|
||||
/**
|
||||
* ln -sfv /usr/bin/php73 /usr/bin/php
|
||||
* php artisan business:store month year
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'user:make_abo_order';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Make Orders from Abos';
|
||||
|
||||
private $timeStart;
|
||||
|
||||
private $month;
|
||||
|
||||
private $year;
|
||||
|
||||
private $sendCreditMail = false;
|
||||
|
||||
private $sendUpdateMail = false;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->timeStart = microtime(true);
|
||||
\Log::channel('cron')->info('UserMakeAboOrder: Befehl gestartet');
|
||||
$this->info('RUN Command user:make_abo_order');
|
||||
|
||||
try {
|
||||
$this->checkAbosToOrder();
|
||||
$executionTime = $this->getExecutionTime();
|
||||
\Log::channel('cron')->info("UserMakeAboOrder: Befehl erfolgreich abgeschlossen in {$executionTime}");
|
||||
$this->info("Befehl erfolgreich abgeschlossen in {$executionTime}");
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
\Log::channel('cron')->error('UserMakeAboOrder: Fehler beim Ausführen des Befehls', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
$this->error('Fehler beim Ausführen des Befehls: ' . $e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft alle Abos, die heute fällig sind und erstellt Bestellungen
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function checkAbosToOrder()
|
||||
{
|
||||
$dateNow = Carbon::now()->format('Y-m-d');
|
||||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Suche nach fälligen Abos für Datum', ['date' => $dateNow]);
|
||||
|
||||
// Prüfe auf bereits verarbeitete Abos am heutigen Tag (Duplikatsprüfung)
|
||||
$userAbos = UserAbo::where('next_date', '=', $dateNow)
|
||||
->where('active', true)
|
||||
->where('status', '=', 2) // abo_okay
|
||||
->whereDoesntHave('user_abo_orders', function ($query) use ($dateNow) {
|
||||
$query->whereDate('created_at', $dateNow);
|
||||
})
|
||||
->get();
|
||||
|
||||
$count = $userAbos->count();
|
||||
\Log::channel('abo_order')->info("UserMakeAboOrder: {$count} fällige Abos gefunden (ohne bereits verarbeitete)");
|
||||
$this->info("Gefundene fällige Abos: {$count}");
|
||||
|
||||
foreach ($userAbos as $userAbo) {
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Verarbeite Abo', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'payone_userid' => $userAbo->payone_userid,
|
||||
]);
|
||||
|
||||
$this->info("Verarbeite Abo: {$userAbo->id} (PayoneUserid: {$userAbo->payone_userid})");
|
||||
|
||||
try {
|
||||
// Locking-Mechanismus: Verhindert Race Conditions bei paralleler Ausführung
|
||||
$shoppingOrder = DB::transaction(function () use ($userAbo, $dateNow) {
|
||||
// Lock das Abo für Update, um Race Conditions zu vermeiden
|
||||
$lockedAbo = UserAbo::where('id', $userAbo->id)
|
||||
->where('next_date', '=', $dateNow)
|
||||
->where('active', true)
|
||||
->where('status', '=', 2) // abo_okay
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if (! $lockedAbo) {
|
||||
\Log::channel('abo_order')->warning('UserMakeAboOrder: Abo wurde bereits verarbeitet oder ist nicht mehr aktiv', [
|
||||
'abo_id' => $userAbo->id,
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Nochmalige Prüfung auf Duplikat innerhalb der Transaktion
|
||||
$existingOrder = UserAboOrder::where('user_abo_id', $lockedAbo->id)
|
||||
->whereDate('created_at', $dateNow)
|
||||
->first();
|
||||
|
||||
if ($existingOrder) {
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo wurde bereits heute verarbeitet', [
|
||||
'abo_id' => $lockedAbo->id,
|
||||
'existing_order_id' => $existingOrder->shopping_order_id,
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->makeOrder($lockedAbo);
|
||||
}, 3); // 3 Versuche bei Deadlocks
|
||||
|
||||
if ($shoppingOrder) {
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Bestellung erstellt', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
]);
|
||||
$this->info("Bestellung erstellt: {$shoppingOrder->id}");
|
||||
} else {
|
||||
\Log::channel('abo_order')->warning('UserMakeAboOrder: Keine Bestellung erstellt für Abo', ['abo_id' => $userAbo->id]);
|
||||
$this->warn("Keine Bestellung erstellt für Abo: {$userAbo->id}");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Fehler bei der Verarbeitung des Abos', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
$this->error("Fehler bei Abo {$userAbo->id}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine Bestellung für ein Abo
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @return mixed
|
||||
*/
|
||||
private function makeOrder($userAbo)
|
||||
{
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Starte Bestellungserstellung', ['abo_id' => $userAbo->id]);
|
||||
$this->info('Starte Bestellungserstellung für Abo: ' . $userAbo->id);
|
||||
|
||||
$shoppingOrder = null;
|
||||
$userOrder = new UserMakeOrder($userAbo);
|
||||
|
||||
try {
|
||||
if (! $userOrder->createShoppingUser()) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Konnte Shopping-User nicht erstellen', ['abo_id' => $userAbo->id]);
|
||||
$this->error("Konnte Shopping-User für Abo {$userAbo->id} nicht erstellen");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$shoppingOrder = $userOrder->makeShoppingOrder();
|
||||
if (! $shoppingOrder) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Konnte Bestellung nicht erstellen', ['abo_id' => $userAbo->id]);
|
||||
$this->error("Konnte Bestellung für Abo {$userAbo->id} nicht erstellen");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Bestellung erstellt, starte Zahlungsvorgang', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
]);
|
||||
|
||||
$response = $userOrder->makePayment();
|
||||
$this->info('makePayment response: ' . json_encode($response));
|
||||
|
||||
// Prüfe ob Response ein Array ist (kann auch Objekt sein)
|
||||
if (is_object($response)) {
|
||||
$response = (array) $response;
|
||||
}
|
||||
|
||||
if (! isset($response['status'])) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Ungültige Zahlungsantwort', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'response' => $response,
|
||||
]);
|
||||
$this->error("Ungültige Zahlungsantwort für Abo {$userAbo->id}");
|
||||
|
||||
// Bei fehlender Status-Information: Abo nicht aktualisieren, damit es beim nächsten Lauf erneut versucht wird
|
||||
// Aber Bestellung speichern für Nachverfolgung
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Ungültige Zahlungsantwort - kein Status');
|
||||
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
if ($response['status'] === 'APPROVED') {
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Zahlung erfolgreich', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'response' => $response,
|
||||
]);
|
||||
$this->info("Zahlung erfolgreich für Abo {$userAbo->id}");
|
||||
// Nur bei erfolgreicher Zahlung: next_date aktualisieren
|
||||
$this->updateAbo($userAbo, $shoppingOrder, 1);
|
||||
} elseif ($response['status'] === 'ERROR') {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Zahlungsfehler', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'error' => $response,
|
||||
]);
|
||||
$this->error("Zahlungsfehler für Abo {$userAbo->id}");
|
||||
|
||||
MyLog::writeLog(
|
||||
'userabo',
|
||||
'error',
|
||||
'Error:3002 App\Console\Commands\UserMakeAboOrder::makeOrder / makePayment Error response',
|
||||
$response
|
||||
);
|
||||
|
||||
// Bei Zahlungsfehler: Status setzen, aber next_date NICHT aktualisieren
|
||||
// Damit wird das Abo beim nächsten Cron-Lauf erneut versucht
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 3, $response);
|
||||
|
||||
$shoppingPayment = $userOrder->getShoppingPayment();
|
||||
if ($shoppingPayment) {
|
||||
$data = [
|
||||
'mode' => $shoppingPayment->mode,
|
||||
'txaction' => 'error',
|
||||
'send_link' => false,
|
||||
'payment_error' => $response,
|
||||
];
|
||||
|
||||
Payment::paymentStatusSendMail($shoppingOrder, $shoppingPayment, $data);
|
||||
}
|
||||
} elseif ($response['status'] === 'PENDING' || $response['status'] === 'REDIRECT') {
|
||||
// Pending/Redirect Status: Bestellung speichern, aber Abo nicht aktualisieren
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Zahlung ausstehend/weiterleitung', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $response['status'],
|
||||
]);
|
||||
$this->info("Zahlung ausstehend für Abo {$userAbo->id}: {$response['status']}");
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Zahlung ausstehend: ' . $response['status']);
|
||||
} else {
|
||||
// Unbekannter Status: Bestellung speichern, aber Abo nicht aktualisieren
|
||||
\Log::channel('abo_order')->warning('UserMakeAboOrder: Unbekannter Zahlungsstatus', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $response['status'],
|
||||
]);
|
||||
$this->warn("Unbekannter Zahlungsstatus für Abo {$userAbo->id}: {$response['status']}");
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Unbekannter Status: ' . $response['status']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Ausnahme bei der Bestellungserstellung', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
$this->error("Ausnahme bei Abo {$userAbo->id}: " . $e->getMessage());
|
||||
|
||||
// Bestellung existiert (z. B. Fehler bei Payone): Abo-Fehlerstatus, Bestellung bleibt nachvollziehbar
|
||||
if ($shoppingOrder) {
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Exception: ' . $e->getMessage());
|
||||
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
// Noch keine ShoppingOrder (createShoppingUser / makeShoppingOrder): Exception durchreichen,
|
||||
// sonst ruft der Aufrufer nur "null" ohne Ursache (z. B. Testbench, fehlende country_id im Yard).
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Abo nach einer erfolgreichen Bestellung
|
||||
* Aktualisiert next_date für den nächsten Abo-Zyklus
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @param mixed $shoppingOrder
|
||||
* @param int $status
|
||||
* @return void
|
||||
*/
|
||||
private function updateAbo($userAbo, $shoppingOrder, $status = 1)
|
||||
{
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Aktualisiere Abo nach erfolgreicher Zahlung', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $status,
|
||||
]);
|
||||
|
||||
$this->info("Aktualisiere Abo: {$userAbo->id} mit Status {$status}");
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($userAbo, $shoppingOrder, $status) {
|
||||
$updateData = [
|
||||
'next_date' => AboHelper::setNextDate(now(), $userAbo->abo_interval),
|
||||
'last_date' => now(),
|
||||
];
|
||||
|
||||
if ($status !== 1) {
|
||||
$updateData['status'] = $status;
|
||||
}
|
||||
|
||||
$userAbo->update($updateData);
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => $status,
|
||||
'paid' => true,
|
||||
]);
|
||||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo erfolgreich aktualisiert', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'next_date' => $updateData['next_date'],
|
||||
]);
|
||||
});
|
||||
|
||||
// Wie bei Payment::paymentStatusPaidAction: Incentive nur wenn Callback nicht lief
|
||||
// (firstOrCreate verhindert Doppelungen wenn Payone später noch trackt)
|
||||
IncentiveTracker::trackAboActivated($shoppingOrder);
|
||||
} catch (\Exception $e) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Fehler beim Aktualisieren des Abos', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
$this->error("Fehler beim Aktualisieren des Abos {$userAbo->id}: " . $e->getMessage());
|
||||
throw $e; // Re-throw für besseres Error-Handling
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Abo bei Fehlern - OHNE next_date zu aktualisieren
|
||||
* Damit wird das Abo beim nächsten Cron-Lauf erneut versucht
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @param mixed $shoppingOrder
|
||||
* @param int|string $status Status-Code oder Fehlermeldung
|
||||
* @param array|null $errorResponse Optionale Fehlerantwort von Payment
|
||||
* @return void
|
||||
*/
|
||||
private function updateAboOnError($userAbo, $shoppingOrder, $status, $errorResponse = null)
|
||||
{
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Aktualisiere Abo bei Fehler (ohne next_date)', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $status,
|
||||
]);
|
||||
|
||||
$this->info("Aktualisiere Abo bei Fehler: {$userAbo->id} (Status: {$status})");
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($userAbo, $shoppingOrder, $status) {
|
||||
// Nur last_date aktualisieren, next_date bleibt unverändert
|
||||
// Damit wird das Abo beim nächsten Cron-Lauf erneut versucht
|
||||
$updateData = [
|
||||
'last_date' => now(),
|
||||
];
|
||||
|
||||
// Status nur setzen wenn es ein numerischer Wert ist
|
||||
if (is_numeric($status)) {
|
||||
$updateData['status'] = $status;
|
||||
}
|
||||
|
||||
$userAbo->update($updateData);
|
||||
|
||||
// UserAboOrder mit Fehlerstatus speichern
|
||||
$orderStatus = is_numeric($status) ? $status : 3; // Default zu 3 (abo_hold) wenn String
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => $orderStatus,
|
||||
'paid' => false,
|
||||
]);
|
||||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo bei Fehler aktualisiert (next_date unverändert)', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'next_date' => $userAbo->next_date,
|
||||
'status' => $status,
|
||||
]);
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Fehler beim Aktualisieren des Abos bei Fehler', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
$this->error("Fehler beim Aktualisieren des Abos {$userAbo->id}: " . $e->getMessage());
|
||||
// Bei Fehler hier nicht re-throw, damit der Hauptprozess fortgesetzt werden kann
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die Ausführungszeit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getExecutionTime()
|
||||
{
|
||||
$diff = microtime(true) - $this->timeStart;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
return $sec . ' Sekunden und ' . round($micro * 1000, 2) . ' ms';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\UserUtil;
|
||||
use App\User;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UserRestore extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'user:restore {user_id}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'User Restore: Reactivates an inactive user and restores their downline structure';
|
||||
|
||||
private $timeStart;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('RUN Command user:restore');
|
||||
\Log::channel('cleanup')->info('COMMAND [user:restore] started.');
|
||||
|
||||
$this->timeStart = microtime(true);
|
||||
|
||||
$result = $this->restoreInactiveUsers();
|
||||
|
||||
\Log::channel('cleanup')->info('COMMAND [user:restore] finished.');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stellt einen deaktivierten User wieder her
|
||||
* - Reaktiviert den User (setzt active=true, stellt m_sponsor wieder her)
|
||||
* - Stellt die Vertriebspartner-Kinder (Downline) wieder her
|
||||
* - Nutzt UserCleanUpLog um die ursprüngliche Struktur wiederherzustellen
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function restoreInactiveUsers()
|
||||
{
|
||||
$methodStartTime = microtime(true);
|
||||
$this->info('START Command restoreInactiveUsers');
|
||||
|
||||
$user_id = $this->argument('user_id');
|
||||
|
||||
if (! $user_id) {
|
||||
$this->error('ERROR: No user_id provided as argument');
|
||||
\Log::channel('cleanup')->error('restoreInactiveUsers: No user_id provided');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info('Restoring user with ID: '.$user_id);
|
||||
|
||||
$user = User::find($user_id);
|
||||
|
||||
if (! $user) {
|
||||
$this->error('ERROR: User not found with ID: '.$user_id);
|
||||
\Log::channel('cleanup')->error('restoreInactiveUsers: User not found, user_id: '.$user_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Prüfe ob User bereits aktiv ist
|
||||
if ($user->active) {
|
||||
$this->warn('WARNING: User is already active, user_id: '.$user_id);
|
||||
\Log::channel('cleanup')->warning('restoreInactiveUsers: User is already active, user_id: '.$user_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
\DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$data = [
|
||||
'user_id' => $user->id,
|
||||
'email' => $user->email,
|
||||
'm_account' => $user->account ? $user->account->m_account : '',
|
||||
'm_first_name' => $user->account ? $user->account->m_first_name : '',
|
||||
'm_last_name' => $user->account ? $user->account->m_last_name : '',
|
||||
];
|
||||
|
||||
// Reaktiviere User (setzt active=true, stellt m_sponsor aus pre_sponsor wieder her)
|
||||
UserUtil::reactiveUser($user);
|
||||
|
||||
// Stelle alle Vertriebspartner-Kinder wieder her
|
||||
UserUtil::resetChildsToSponsor($user->id);
|
||||
|
||||
\DB::commit();
|
||||
|
||||
$diff = microtime(true) - $methodStartTime;
|
||||
$sec = intval($diff);
|
||||
$micro = $diff - $sec;
|
||||
|
||||
$this->info('SUCCESS: User restored successfully');
|
||||
$this->info('END Command restoreInactiveUsers | Time: '.$sec.'sec :'.round($micro * 1000, 4).' ms');
|
||||
|
||||
\Log::channel('cleanup')->info('restoreInactiveUsers SUCCESS: '.json_encode($data));
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollBack();
|
||||
|
||||
$this->error('ERROR: Failed to restore user: '.$e->getMessage());
|
||||
\Log::channel('cleanup')->error('restoreInactiveUsers FAILED for user_id: '.$user_id.' | Error: '.$e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
app/Console/Kernel.php
Normal file → Executable file
37
app/Console/Kernel.php
Normal file → Executable file
|
|
@ -3,11 +3,6 @@
|
|||
namespace App\Console;
|
||||
|
||||
use App\Console\Commands\BusinessStore;
|
||||
use App\Console\Commands\BusinessStoreOptimized;
|
||||
use App\Console\Commands\CheckPaymentsAccount;
|
||||
use App\Console\Commands\DhlUpdateTracking;
|
||||
use App\Console\Commands\UserCleanup;
|
||||
use App\Console\Commands\UserMakeAboOrder;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
|
|
@ -20,44 +15,18 @@ class Kernel extends ConsoleKernel
|
|||
*/
|
||||
protected $commands = [
|
||||
BusinessStore::class,
|
||||
BusinessStoreOptimized::class,
|
||||
CheckPaymentsAccount::class,
|
||||
UserMakeAboOrder::class,
|
||||
UserCleanup::class,
|
||||
DhlUpdateTracking::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// Job 1: Überprüft täglich um 02:00 Uhr die Zahlungskonten.
|
||||
$schedule->command('payments:check-accounts')->dailyAt('02:00');
|
||||
// Jobs 2, 3, 4: Die Befehle aus deinem alten Shell-Skript.
|
||||
// Werden nacheinander täglich zu unterschiedlichen Zeiten ausgeführt,
|
||||
// um die Serverlast zu verteilen.
|
||||
$schedule->command('business:store-optimized 0 0')->dailyAt('03:00');
|
||||
|
||||
$schedule->command('user:cleanup')->dailyAt('03:30');
|
||||
$schedule->command('user:make_abo_order')->dailyAt('04:00');
|
||||
|
||||
// Abo-Chart-Snapshots: vergangene Monate einfrieren (nach allen Abo-Jobs)
|
||||
$schedule->command('abo:store-chart-snapshots')->dailyAt('04:30');
|
||||
|
||||
// Incentive: Punkteberechnung täglich nach business:store-optimized
|
||||
$schedule->command('incentive:calculate')->dailyAt('05:00');
|
||||
|
||||
// Cleanup old log files weekly (keeps logs for 30 days)
|
||||
$schedule->command('logs:cleanup --days=30')->weekly()->sundays()->at('05:00');
|
||||
|
||||
// DHL Tracking Update: Stündlich mit status-basierten Intervallen und Batch-API
|
||||
$schedule->command('dhl:update-tracking --days=30 --send-emails')
|
||||
->hourly()
|
||||
->withoutOverlapping()
|
||||
->runInBackground();
|
||||
// $schedule->command('inspire')
|
||||
// ->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Cron;
|
||||
|
||||
use App\User;
|
||||
use stdClass;
|
||||
use App\Models\UserBusinessStructure;
|
||||
use App\Services\BusinessPlan\TreeCalcBotOptimized;
|
||||
use App\Services\BusinessPlan\TreeCalcBot;
|
||||
|
||||
class BusinessUsersStore
|
||||
{
|
||||
|
|
@ -15,7 +14,7 @@ class BusinessUsersStore
|
|||
private $user_business_structure;
|
||||
private $users_structure = [];
|
||||
|
||||
|
||||
|
||||
public function __construct($month, $year)
|
||||
{
|
||||
$this->month = $month;
|
||||
|
|
@ -23,17 +22,16 @@ class BusinessUsersStore
|
|||
}
|
||||
|
||||
|
||||
public function getStoreUserBusinessStructure()
|
||||
{
|
||||
public function getStoreUserBusinessStructure(){
|
||||
return UserBusinessStructure::where('year', $this->year)->where('month', $this->month)->first();
|
||||
}
|
||||
|
||||
public function storeUserBusinessStructure()
|
||||
{
|
||||
if ($this->user_business_structure = $this->getStoreUserBusinessStructure()) {
|
||||
if($this->user_business_structure = $this->getStoreUserBusinessStructure()){
|
||||
return $this->user_business_structure;
|
||||
}
|
||||
$treeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'admin');
|
||||
$treeCalcBot = new TreeCalcBot($this->month, $this->year, 'admin');
|
||||
//only load, when no structur is save
|
||||
$treeCalcBot->initStructureAdmin(false);
|
||||
$this->storeStructure($treeCalcBot);
|
||||
|
|
@ -41,34 +39,31 @@ class BusinessUsersStore
|
|||
|
||||
public function storeBusinessUsersDetail()
|
||||
{
|
||||
if (!$this->user_business_structure) {
|
||||
if(!$this->user_business_structure){
|
||||
$this->user_business_structure = $this->getStoreUserBusinessStructure();
|
||||
if (!$this->user_business_structure) {
|
||||
if(!$this->user_business_structure){
|
||||
abort(403, 'not found UserBusinessStructure');
|
||||
}
|
||||
}
|
||||
foreach ($this->user_business_structure->users as $user_id => $completed) {
|
||||
if ($completed === 0) {
|
||||
$user = User::find($user_id);
|
||||
if ($user) {
|
||||
$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'admin');
|
||||
$TreeCalcBot->initBusinesslUserDetail($user);
|
||||
if (!$TreeCalcBot->business_user) {
|
||||
abort(403, 'not found TreeCalcBot->business_user');
|
||||
}
|
||||
$this->storeBusinesslUser($TreeCalcBot->business_user);
|
||||
$users = $this->user_business_structure->users;
|
||||
$users[$user_id] = 1;
|
||||
$this->user_business_structure->users = $users;
|
||||
$this->user_business_structure->save();
|
||||
foreach($this->user_business_structure->users as $user_id=>$completed){
|
||||
if($completed === 0){
|
||||
$user = User::findOrFail($user_id);
|
||||
$TreeCalcBot = new TreeCalcBot($this->month, $this->year, 'admin');
|
||||
$TreeCalcBot->initBusinesslUserDetail($user);
|
||||
if(!$TreeCalcBot->business_user){
|
||||
abort(403, 'not found TreeCalcBot->business_user');
|
||||
}
|
||||
$this->storeBusinesslUser($TreeCalcBot->business_user);
|
||||
$users = $this->user_business_structure->users;
|
||||
$users[$user_id] = 1;
|
||||
$this->user_business_structure->users = $users;
|
||||
$this->user_business_structure->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function storeBusinesslUser($business_user)
|
||||
{
|
||||
public function storeBusinesslUser($business_user){
|
||||
$b_user = $business_user->getBUser();
|
||||
$b_user->user_items = $this->storeUserItems($business_user->businessUserItems, 1);
|
||||
$b_user->b_structure_id = $this->user_business_structure->id;
|
||||
|
|
@ -76,13 +71,12 @@ class BusinessUsersStore
|
|||
}
|
||||
|
||||
|
||||
public function storeBusinessCompleted()
|
||||
{
|
||||
if (!$this->user_business_structure) {
|
||||
public function storeBusinessCompleted(){
|
||||
if(!$this->user_business_structure){
|
||||
$this->user_business_structure = $this->getStoreUserBusinessStructure();
|
||||
}
|
||||
foreach ($this->user_business_structure->users as $user_id => $completed) {
|
||||
if ($completed === 0) {
|
||||
foreach($this->user_business_structure->users as $user_id=>$completed){
|
||||
if($completed === 0){
|
||||
return false;
|
||||
}
|
||||
$this->user_business_structure->completed = 1;
|
||||
|
|
@ -90,22 +84,21 @@ class BusinessUsersStore
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function storeUserItems($userItems, $line)
|
||||
{
|
||||
private function storeUserItems($userItems, $line){
|
||||
$ret = [];
|
||||
foreach ($userItems as $userItem) {
|
||||
foreach($userItems as $userItem){
|
||||
$temp = null;
|
||||
if (count($userItem->businessUserItems) > 0) {
|
||||
$temp = $this->storeUserItems($userItem->businessUserItems, $line + 1);
|
||||
if(count($userItem->businessUserItems) > 0){
|
||||
$temp = $this->storeUserItems($userItem->businessUserItems, $line+1);
|
||||
}
|
||||
$obj = new stdClass();
|
||||
$obj->user_id = $userItem->user_id;
|
||||
$obj->line = $line;
|
||||
$obj->points = $userItem->sales_volume_points_sum;
|
||||
$obj->parents = $temp;
|
||||
$ret[] = $obj;
|
||||
$ret[] = $obj;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
|
@ -118,13 +111,13 @@ class BusinessUsersStore
|
|||
}*/
|
||||
|
||||
$structure = [];
|
||||
foreach ($treeCalcBot->business_users as $business_user) {
|
||||
foreach($treeCalcBot->business_users as $business_user){
|
||||
$structure[] = $this->storeStructureItem($business_user, 0);
|
||||
}
|
||||
|
||||
$parentless = [];
|
||||
if ($treeCalcBot->parentless) {
|
||||
foreach ($treeCalcBot->parentless as $pless) {
|
||||
if($treeCalcBot->parentless){
|
||||
foreach($treeCalcBot->parentless as $pless){
|
||||
$parentless[] = $this->storeStructureItem($pless, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -141,15 +134,14 @@ class BusinessUsersStore
|
|||
return $this->user_business_structure;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private function storeStructureItem($item, $deep)
|
||||
{
|
||||
private function storeStructureItem($item, $deep){
|
||||
$temp = null;
|
||||
if ($item->businessUserItems) {
|
||||
foreach ($item->businessUserItems as $parent) {
|
||||
$temp[] = $this->storeStructureItem($parent, $deep + 1);
|
||||
}
|
||||
if($item->businessUserItems){
|
||||
foreach($item->businessUserItems as $parent){
|
||||
$temp[] = $this->storeStructureItem($parent, $deep+1);
|
||||
}
|
||||
}
|
||||
$this->users_structure[$item->user_id] = 0;
|
||||
$obj = new stdClass();
|
||||
|
|
@ -161,4 +153,7 @@ class BusinessUsersStore
|
|||
$obj->parents = $temp;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,263 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Cron;
|
||||
|
||||
use App\User;
|
||||
use stdClass;
|
||||
use App\Models\UserBusinessStructure;
|
||||
use App\Services\BusinessPlan\TreeCalcBotOptimized;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class BusinessUsersStoreOptimized
|
||||
{
|
||||
private $month;
|
||||
private $year;
|
||||
private $user_business_structure;
|
||||
private $users_structure = [];
|
||||
private $logger;
|
||||
|
||||
public function __construct($month, $year, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->month = $month;
|
||||
$this->year = $year;
|
||||
$this->logger = $logger ?? app(LoggerInterface::class);
|
||||
}
|
||||
|
||||
public function getStoreUserBusinessStructure()
|
||||
{
|
||||
return UserBusinessStructure::where('year', $this->year)
|
||||
->where('month', $this->month)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function storeUserBusinessStructure()
|
||||
{
|
||||
if ($this->user_business_structure = $this->getStoreUserBusinessStructure()) {
|
||||
$this->logger->info("Found existing business structure for {$this->month}/{$this->year}");
|
||||
return $this->user_business_structure;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->logger->info("Creating new business structure for {$this->month}/{$this->year}");
|
||||
$startTime = microtime(true);
|
||||
|
||||
// Verwende TreeCalcBotOptimized mit Live-Berechnung für aktuelle Daten
|
||||
$treeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'admin', true);
|
||||
$treeCalcBot->initStructureAdmin(false, true); // forceLiveCalculation = true
|
||||
|
||||
$this->storeStructure($treeCalcBot);
|
||||
|
||||
$endTime = microtime(true);
|
||||
$duration = round(($endTime - $startTime) * 1000, 2);
|
||||
$this->logger->info("Business structure created in {$duration}ms with " . count($this->users_structure) . " users");
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error creating business structure: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function storeBusinessUsersDetail()
|
||||
{
|
||||
if (!$this->user_business_structure) {
|
||||
$this->user_business_structure = $this->getStoreUserBusinessStructure();
|
||||
if (!$this->user_business_structure) {
|
||||
throw new \Exception('UserBusinessStructure not found');
|
||||
}
|
||||
}
|
||||
|
||||
$totalUsers = count($this->user_business_structure->users);
|
||||
$processedUsers = 0;
|
||||
|
||||
$this->logger->info("Processing {$totalUsers} business user details");
|
||||
|
||||
foreach ($this->user_business_structure->users as $user_id => $completed) {
|
||||
if ($completed === 0) {
|
||||
try {
|
||||
$user = User::find($user_id);
|
||||
if ($user) {
|
||||
$this->processBusinessUser($user, $user_id);
|
||||
$processedUsers++;
|
||||
|
||||
// Log progress every 50 users
|
||||
if ($processedUsers % 50 === 0) {
|
||||
$this->logger->info("Processed {$processedUsers}/{$totalUsers} business users");
|
||||
}
|
||||
} else {
|
||||
$this->logger->warning("User {$user_id} not found, skipping");
|
||||
$this->markUserCompleted($user_id);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error processing user {$user_id}: " . $e->getMessage());
|
||||
// Mark as completed to avoid infinite retry loops
|
||||
$this->markUserCompleted($user_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->info("Completed processing {$processedUsers} business user details");
|
||||
}
|
||||
|
||||
private function processBusinessUser(User $user, int $user_id): void
|
||||
{
|
||||
try {
|
||||
$startTime = microtime(true);
|
||||
|
||||
// Verwende TreeCalcBotOptimized für detaillierte Benutzerberechnung
|
||||
$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'admin', true);
|
||||
$TreeCalcBot->initBusinesslUserDetail($user, true); // forceLiveCalculation = true
|
||||
|
||||
if (!$TreeCalcBot->business_user) {
|
||||
throw new \Exception("business_user not found for user {$user_id}");
|
||||
}
|
||||
|
||||
$this->storeBusinesslUser($TreeCalcBot->business_user);
|
||||
$this->markUserCompleted($user_id);
|
||||
|
||||
$endTime = microtime(true);
|
||||
$duration = round(($endTime - $startTime) * 1000, 2);
|
||||
$this->logger->debug("Processed user {$user_id} in {$duration}ms");
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error in processBusinessUser for {$user_id}: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function markUserCompleted(int $user_id): void
|
||||
{
|
||||
$users = $this->user_business_structure->users;
|
||||
$users[$user_id] = 1;
|
||||
$this->user_business_structure->users = $users;
|
||||
$this->user_business_structure->save();
|
||||
}
|
||||
|
||||
public function storeBusinesslUser($business_user)
|
||||
{
|
||||
try {
|
||||
$b_user = $business_user->getBUser();
|
||||
$b_user->user_items = $this->storeUserItems($business_user->businessUserItems, 1);
|
||||
$b_user->b_structure_id = $this->user_business_structure->id;
|
||||
$b_user->save();
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error storing business user: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function storeBusinessCompleted()
|
||||
{
|
||||
if (!$this->user_business_structure) {
|
||||
$this->user_business_structure = $this->getStoreUserBusinessStructure();
|
||||
}
|
||||
|
||||
$incompleteCount = 0;
|
||||
foreach ($this->user_business_structure->users as $user_id => $completed) {
|
||||
if ($completed === 0) {
|
||||
$incompleteCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($incompleteCount === 0) {
|
||||
$this->user_business_structure->completed = 1;
|
||||
$this->user_business_structure->save();
|
||||
$this->logger->info("Business structure marked as completed");
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->logger->info("{$incompleteCount} users still incomplete");
|
||||
return false;
|
||||
}
|
||||
|
||||
private function storeUserItems($userItems, $line)
|
||||
{
|
||||
$ret = [];
|
||||
|
||||
try {
|
||||
foreach ($userItems as $userItem) {
|
||||
$temp = null;
|
||||
if (count($userItem->businessUserItems) > 0) {
|
||||
$temp = $this->storeUserItems($userItem->businessUserItems, $line + 1);
|
||||
}
|
||||
|
||||
$obj = new stdClass();
|
||||
$obj->user_id = $userItem->user_id;
|
||||
$obj->line = $line;
|
||||
$obj->points = $userItem->sales_volume_points_sum ?? 0;
|
||||
$obj->parents = $temp;
|
||||
$ret[] = $obj;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error storing user items at line {$line}: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function storeStructure($treeCalcBot)
|
||||
{
|
||||
try {
|
||||
$structure = [];
|
||||
$businessUsers = $treeCalcBot->business_users;
|
||||
|
||||
if (!is_array($businessUsers)) {
|
||||
throw new \Exception("business_users is not an array");
|
||||
}
|
||||
|
||||
foreach ($businessUsers as $business_user) {
|
||||
$structure[] = $this->storeStructureItem($business_user, 0);
|
||||
}
|
||||
|
||||
$parentless = [];
|
||||
$parentlessUsers = $treeCalcBot->parentless;
|
||||
|
||||
if ($parentlessUsers && is_array($parentlessUsers)) {
|
||||
foreach ($parentlessUsers as $pless) {
|
||||
$parentless[] = $this->storeStructureItem($pless, 0);
|
||||
}
|
||||
}
|
||||
|
||||
$fill = [
|
||||
'month' => $this->month,
|
||||
'year' => $this->year,
|
||||
'structure' => $structure,
|
||||
'parentless' => $parentless,
|
||||
'users' => $this->users_structure,
|
||||
'completed' => false,
|
||||
'status' => 0
|
||||
];
|
||||
|
||||
$this->user_business_structure = UserBusinessStructure::create($fill);
|
||||
$this->logger->info("Stored structure with " . count($structure) . " root users and " . count($parentless) . " parentless users");
|
||||
|
||||
return $this->user_business_structure;
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error storing structure: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function storeStructureItem($item, $deep)
|
||||
{
|
||||
try {
|
||||
$temp = null;
|
||||
if (isset($item->businessUserItems) && is_array($item->businessUserItems)) {
|
||||
foreach ($item->businessUserItems as $parent) {
|
||||
$temp[] = $this->storeStructureItem($parent, $deep + 1);
|
||||
}
|
||||
}
|
||||
|
||||
$this->users_structure[$item->user_id] = 0;
|
||||
|
||||
$obj = new stdClass();
|
||||
$obj->user_id = $item->user_id;
|
||||
$obj->email = $item->email ?? 'unknown';
|
||||
$obj->deep = $deep;
|
||||
$obj->parents = $temp;
|
||||
|
||||
return $obj;
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error storing structure item for user {$item->user_id}: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,154 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Cron;
|
||||
|
||||
use App\User;
|
||||
use App\Models\UserBusiness;
|
||||
use App\Models\UserLevel;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\Models\UserCreditItem;
|
||||
use App\Mail\MailUserLevelUpdate;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Repositories\CreditRepository;
|
||||
|
||||
class UserLevelUpdate
|
||||
{
|
||||
private $month;
|
||||
private $year;
|
||||
private $userLevels;
|
||||
|
||||
|
||||
public function __construct($month, $year)
|
||||
{
|
||||
$this->month = $month;
|
||||
$this->year = $year;
|
||||
// Lade UserLevels für POS-Vergleich (wird für Prüfung benötigt)
|
||||
$this->userLevels = UserLevel::where('active', 1)->orderBy('pos')->get()->keyBy('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt alle UserBusiness Einträge die Level-Updates benötigen
|
||||
* Mit Eager Loading für bessere Performance
|
||||
*/
|
||||
public function getUserBusinessByMonthYear()
|
||||
{
|
||||
return UserBusiness::select('user_businesses.*')
|
||||
->with('user') // Eager Loading für User
|
||||
|
||||
public function getUserBusinessByMonthYear(){
|
||||
return UserBusiness::select('user_businesses.*')
|
||||
->where('user_businesses.month', '=', $this->month)
|
||||
->where('user_businesses.year', '=', $this->year)
|
||||
->whereNotNull('user_businesses.next_qual_user_level')
|
||||
->whereRaw("JSON_LENGTH(user_businesses.next_qual_user_level) > 0")
|
||||
->get();
|
||||
->where('user_businesses.next_qual_user_level', '!=', NULL)
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das User-Level basierend auf next_qual_user_level
|
||||
* Berücksichtigt Arrays und einzelne Level-Objekte
|
||||
* Prüft ob das neue Level höher ist als das aktuelle
|
||||
*/
|
||||
public function makeUserLevelUpdate(UserBusiness $userBusiness, $send_update_mail = false)
|
||||
{
|
||||
public function makeUserLevelUpdate(UserBusiness $userBusiness, $send_update_mail){
|
||||
$ret = false;
|
||||
|
||||
if (!$userBusiness->user) {
|
||||
Log::warning("UserLevelUpdate: UserBusiness {$userBusiness->id} hat kein User-Objekt");
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$nextQualUserLevel = $userBusiness->next_qual_user_level;
|
||||
|
||||
if (!is_array($nextQualUserLevel) || empty($nextQualUserLevel)) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// next_qual_user_level ist ein einzelnes Level-Objekt
|
||||
if (is_array($nextQualUserLevel) && isset($nextQualUserLevel['id'])) {
|
||||
// return wenn bereits aktualisierte Level
|
||||
if (isset($nextQualUserLevel['hasUpdated']) && $nextQualUserLevel['hasUpdated'] == 1) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$newLevelId = $nextQualUserLevel['id'];
|
||||
$newLevelPos = null;
|
||||
|
||||
// Lade Level-Objekt für POS-Vergleich
|
||||
$newLevel = $this->userLevels->get($newLevelId);
|
||||
if ($newLevel) {
|
||||
$newLevelPos = $newLevel->pos;
|
||||
}
|
||||
|
||||
// Prüfe ob das neue Level höher ist als das aktuelle
|
||||
$currentUserLevel = null;
|
||||
if ($userBusiness->user->m_level) {
|
||||
$currentUserLevel = $this->userLevels->get($userBusiness->user->m_level);
|
||||
}
|
||||
|
||||
// Nur updaten wenn das neue Level höher ist (POS > aktuelles Level POS)
|
||||
if (!$currentUserLevel || !$newLevelPos) {
|
||||
return $ret;
|
||||
}
|
||||
if ($newLevelPos <= $currentUserLevel->pos) {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Update durchführen wenn ein höheres Level gefunden wurde
|
||||
try {
|
||||
$userBusiness->user->m_level = $newLevel['id'];
|
||||
if(!isset($nextQualUserLevel['hasUpdated']) && $userBusiness->user){
|
||||
$userBusiness->user->m_level = $nextQualUserLevel['id'];
|
||||
$userBusiness->user->save();
|
||||
|
||||
// Markiere das Level als aktualisiert in next_qual_user_level
|
||||
$nextQualUserLevel['hasUpdated'] = 1;
|
||||
$userBusiness->next_qual_user_level = $nextQualUserLevel;
|
||||
$userBusiness->save();
|
||||
|
||||
$ret = $newLevelId . ' ' . ($newLevel->name ?? 'Unbekannt');
|
||||
|
||||
if ($send_update_mail) {
|
||||
try {
|
||||
$this->sendUpdateMail(
|
||||
$userBusiness->user,
|
||||
$userBusiness->payline_points_qual_kp ?? 0,
|
||||
$newLevel->name ?? 'Unbekannt'
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("UserLevelUpdate: E-Mail konnte nicht gesendet werden für User {$userBusiness->user->id}: " . $e->getMessage());
|
||||
// E-Mail-Fehler sollten das Update nicht verhindern
|
||||
}
|
||||
$ret = $nextQualUserLevel['id'].' '.$nextQualUserLevel['name'];
|
||||
if($send_update_mail){
|
||||
self::sendUpdateMail($userBusiness->user, $userBusiness->total_qual_tp, $nextQualUserLevel['name']);
|
||||
}
|
||||
|
||||
Log::info("UserLevelUpdate: User {$userBusiness->user->id} Level aktualisiert zu {$ret}");
|
||||
} catch (\Exception $e) {
|
||||
Log::error("UserLevelUpdate: Fehler beim Update von User {$userBusiness->user->id}: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
private function sendUpdateMail(User $user, $tp, $to)
|
||||
{
|
||||
|
||||
private function sendUpdateMail(User $user, $tp, $to){
|
||||
$bcc = [];
|
||||
$email = $user->email;
|
||||
if (!$email) {
|
||||
if ($user->mode === 'test') {
|
||||
} else {
|
||||
if(!$email){
|
||||
if($user->mode === 'test'){
|
||||
}else{
|
||||
$email = config('app.checkout_mail');
|
||||
}
|
||||
}
|
||||
if ($user->mode === 'test') {
|
||||
if($user->mode === 'test'){
|
||||
$bcc[] = config('app.checkout_test_mail');
|
||||
} else {
|
||||
}else{
|
||||
$bcc[] = config('app.checkout_mail');
|
||||
}
|
||||
if (\App\Services\Util::isTestSystem()) {
|
||||
$email = config('app.checkout_test_mail');
|
||||
$bcc[] = config('app.checkout_test_mail');
|
||||
}
|
||||
Mail::to($email)->bcc($bcc)->locale($user->getLocale())->send(new MailUserLevelUpdate($tp, $to));
|
||||
Mail::to($email)->bcc($bcc)->send(new MailUserLevelUpdate($tp, $to));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,268 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Cron;
|
||||
|
||||
use App\Http\Controllers\Pay\PayoneController;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingOrderItem;
|
||||
use App\Models\UserAbo;
|
||||
use App\Services\AboOrderCart;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Yard;
|
||||
|
||||
class UserMakeOrder
|
||||
{
|
||||
private $userAbo;
|
||||
|
||||
private $shopping_user;
|
||||
|
||||
private $shopping_order;
|
||||
|
||||
private $is_for;
|
||||
|
||||
private $user;
|
||||
|
||||
private $pay;
|
||||
|
||||
public function __construct(UserAbo $userAbo)
|
||||
{
|
||||
$this->userAbo = $userAbo;
|
||||
Log::info('UserMakeOrder initialisiert für UserAbo ID: '.$userAbo->id);
|
||||
}
|
||||
|
||||
public function checkProducts()
|
||||
{
|
||||
Log::info('Überprüfe Produkte für UserAbo ID: '.$this->userAbo->id);
|
||||
$ret = [];
|
||||
|
||||
if (! $this->userAbo->items || $this->userAbo->items->isEmpty()) {
|
||||
Log::warning('Keine Artikel für UserAbo ID: '.$this->userAbo->id.' gefunden');
|
||||
|
||||
return $ret;
|
||||
}
|
||||
// preise prüfen, ob sie sich geändert haben?
|
||||
foreach ($this->userAbo->items as $item) {
|
||||
$ret[] = [
|
||||
'product_id' => $item->product_id,
|
||||
'comp' => $item->comp,
|
||||
'qty' => $item->qty,
|
||||
'price' => $item->price,
|
||||
'price_net' => $item->price_net,
|
||||
'tax_rate' => $item->tax_rate,
|
||||
'tax' => $item->tax,
|
||||
'price_vk_net' => $item->price_vk_net,
|
||||
'discount' => $item->discount,
|
||||
'points' => $item->points,
|
||||
];
|
||||
}
|
||||
|
||||
Log::info('Produkte überprüft: '.count($ret).' Artikel gefunden');
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function makePayment($testmode = false)
|
||||
{
|
||||
Log::info('Starte Zahlungsvorgang für UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
try {
|
||||
$this->pay = new PayoneController;
|
||||
$this->pay->init($this->shopping_user, $this->shopping_order);
|
||||
$amount = $this->shopping_order->total_shipping * 100;
|
||||
// $amount = Yard::instance($this->instance)->totalWithShipping(2, '.', '') * 100;
|
||||
|
||||
$this->pay->setAboPayment($this->userAbo, $amount, 'EUR');
|
||||
$this->pay->setPersonalData();
|
||||
$response = $this->pay->onlyPaymentResponse();
|
||||
\Log::info('Response: '.json_encode($response));
|
||||
// $response = $this->pay->ResponseData(true);
|
||||
|
||||
Log::info('Zahlungsvorgang abgeschlossen für UserAbo ID: '.$this->userAbo->id.', Status: '.($response->status ?? 'unbekannt'));
|
||||
|
||||
return $response;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Fehler bei Zahlungsvorgang für UserAbo ID: '.$this->userAbo->id.': '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function getShoppingPayment()
|
||||
{
|
||||
Log::info('Rufe Zahlungsinformationen ab für UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
if ($this->pay) {
|
||||
$payment = $this->pay->getShoppingPayment();
|
||||
Log::info('Zahlungsinformationen abgerufen: '.($payment ? 'erfolgreich' : 'nicht verfügbar'));
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
||||
Log::warning('Keine Zahlungsinformationen verfügbar für UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function createShoppingUser()
|
||||
{
|
||||
Log::info('Erstelle Shopping-User für UserAbo ID: '.$this->userAbo->id);
|
||||
// hier muss der letzte shopping_user verwendet werden
|
||||
try {
|
||||
$this->shopping_user = AboOrderCart::makeCustomerDetail($this->userAbo);
|
||||
$this->shopping_user->created_at = now();
|
||||
$this->shopping_user->updated_at = now();
|
||||
$this->shopping_user->save();
|
||||
|
||||
Log::info('Shopping-User erstellt für UserAbo ID: '.$this->userAbo->id.', Neue User-ID: '.$this->shopping_user->id);
|
||||
|
||||
return $this->shopping_user;
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Fehler beim Erstellen des Shopping-Users für UserAbo ID: '.$this->userAbo->id.': '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function makeShoppingOrder()
|
||||
{
|
||||
Log::info('Erstelle Bestellung für UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
try {
|
||||
if (! $this->shopping_user) {
|
||||
Log::error('Kein Shopping-User verfügbar für Bestellerstellung, UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// WICHTIG: Yard komplett leeren vor jedem Abo, um sicherzustellen, dass keine Produkte
|
||||
// aus vorherigen Abos im Cart bleiben
|
||||
Yard::instance('shopping')->destroy();
|
||||
|
||||
// initYard akzeptiert nur einen Parameter (user_abo)
|
||||
AboOrderCart::initYard($this->userAbo);
|
||||
|
||||
// Nochmalige Sicherheitsprüfung: Yard sollte leer sein
|
||||
$yardBefore = Yard::instance('shopping');
|
||||
$itemsBefore = $yardBefore->content();
|
||||
if ($itemsBefore->count() > 0) {
|
||||
Log::warning('UserMakeOrder: Yard war nicht leer nach initYard für Abo ID: '.$this->userAbo->id.', Items: '.$itemsBefore->count());
|
||||
$yardBefore->destroy(); // Erzwinge Leerung
|
||||
}
|
||||
|
||||
// hier wird die Bestellung erstellt inkl aktueller Preise
|
||||
AboOrderCart::makeOrderYard($this->userAbo);
|
||||
|
||||
$yard = Yard::instance('shopping');
|
||||
|
||||
// Debug: Logge welche Produkte im Cart sind
|
||||
$items = $yard->content();
|
||||
Log::info('UserMakeOrder: Produkte im Cart nach makeOrderYard für Abo ID: '.$this->userAbo->id, [
|
||||
'abo_id' => $this->userAbo->id,
|
||||
'item_count' => $items->count(),
|
||||
'items' => $items->map(function ($item) {
|
||||
return [
|
||||
'product_id' => $item->id,
|
||||
'name' => $item->name,
|
||||
'qty' => $item->qty,
|
||||
'rowId' => $item->rowId,
|
||||
];
|
||||
})->toArray(),
|
||||
]);
|
||||
|
||||
$shoppingUserStamm = $this->userAbo->shopping_user;
|
||||
if (! $shoppingUserStamm) {
|
||||
Log::error('UserAbo ohne shopping_user (Stammdaten-Kunde), UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Referenz fuer Shop/Mode: neueste Bestellung (hasOne shopping_order kann null sein z. B. wenn die
|
||||
// aelteste Order soft-deleted ist oder mehrere Orders existieren).
|
||||
$referenceOrder = $shoppingUserStamm->shopping_orders()
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
if (! $referenceOrder || ! $referenceOrder->user_shop_id) {
|
||||
Log::error('Fehlende Beziehungsdaten fuer Bestellerstellung (Referenz-Bestellung ohne user_shop_id), UserAbo ID: '.$this->userAbo->id, [
|
||||
'shopping_user_id' => $shoppingUserStamm->id,
|
||||
'reference_order_id' => $referenceOrder?->id,
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$countryId = $yard->getShippingCountryId() ?? $referenceOrder->country_id;
|
||||
if (! $countryId) {
|
||||
Log::error('Kein country_id (Yard shipping_country_id und Referenz-Bestellung leer), UserAbo ID: '.$this->userAbo->id, [
|
||||
'yard_shipping_country_id' => $yard->getShippingCountryId(),
|
||||
'reference_order_id' => $referenceOrder->id,
|
||||
'reference_country_id' => $referenceOrder->country_id,
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->shopping_order = ShoppingOrder::create([
|
||||
'shopping_user_id' => $this->shopping_user->id,
|
||||
'member_id' => $this->userAbo->member_id ?? $referenceOrder->member_id,
|
||||
'auth_user_id' => $this->userAbo->is_for === 'me'
|
||||
? ($this->userAbo->user_id ?? $referenceOrder->auth_user_id ?? $this->shopping_user->auth_user_id)
|
||||
: ($this->shopping_user->auth_user_id ?? $referenceOrder->auth_user_id),
|
||||
'country_id' => $countryId,
|
||||
'language' => \App::getLocale(),
|
||||
'user_shop_id' => (int) $referenceOrder->user_shop_id,
|
||||
'payment_for' => $this->shopping_user->getOrderPaymentFor(),
|
||||
'total' => $yard->total(2, '.', ''),
|
||||
'subtotal' => $yard->subtotal(2, '.', ''),
|
||||
'shipping' => $yard->shipping(2, '.', ''),
|
||||
'shipping_net' => $yard->shippingNet(2, '.', ''),
|
||||
'subtotal_ws' => $yard->subtotalWithShipping(2, '.', ''),
|
||||
'tax' => $yard->taxWithShipping(2, '.', ''),
|
||||
'total_shipping' => $yard->totalWithShipping(2, '.', ''),
|
||||
'points' => $yard->points(),
|
||||
'weight' => $yard->weight(),
|
||||
'is_abo' => 1,
|
||||
'abo_interval' => $this->userAbo->abo_interval ?? 0,
|
||||
'txaction' => 'prev',
|
||||
'mode' => $referenceOrder->mode,
|
||||
]);
|
||||
|
||||
Log::info('Bestellung erstellt für UserAbo ID: '.$this->userAbo->id.', Bestellnummer: '.$this->shopping_order->id);
|
||||
|
||||
$items = $yard->getContentByOrder();
|
||||
$itemCount = 0;
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (! ShoppingOrderItem::where('shopping_order_id', $this->shopping_order->id)->where('row_id', $item->rowId)->count()) {
|
||||
$price_net = $yard->rowPriceNet($item, 2, '.', '');
|
||||
$tax = $item->price - $price_net;
|
||||
$data = [
|
||||
'shopping_order_id' => $this->shopping_order->id,
|
||||
'row_id' => $item->rowId,
|
||||
'product_id' => $item->id,
|
||||
'comp' => $item->options->comp,
|
||||
'qty' => $item->qty,
|
||||
'price' => $item->price,
|
||||
'price_net' => $price_net,
|
||||
'tax_rate' => $item->taxRate,
|
||||
'tax' => $tax,
|
||||
'price_vk_net' => $this->shopping_order->getPriceVkNetBy($item->id),
|
||||
'discount' => $item->options->no_commission ? 0 : $this->shopping_order->getUserDiscount(),
|
||||
'points' => $item->options->points,
|
||||
'slug' => $item->options->slug,
|
||||
];
|
||||
ShoppingOrderItem::create($data);
|
||||
$itemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('Bestellpositionen hinzugefügt für UserAbo ID: '.$this->userAbo->id.', Anzahl: '.$itemCount);
|
||||
|
||||
$this->shopping_order->makeTaxSplit();
|
||||
Log::info('Steueraufteilung für Bestellung abgeschlossen, UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
return $this->shopping_order;
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Fehler bei Bestellerstellung für UserAbo ID: '.$this->userAbo->id.': '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,17 +25,15 @@ class UserPaymentCredits
|
|||
->where('user_businesses.month', '=', $this->month)
|
||||
->where('user_businesses.year', '=', $this->year)
|
||||
->where(function($q) {
|
||||
return $q->where('user_businesses.commission_pp_total', '>', 0)
|
||||
return $q->where('user_businesses.commission_team_total', '>', 0)
|
||||
->orWhere('user_businesses.commission_shop_sales', '>', 0);
|
||||
})
|
||||
->get();
|
||||
}
|
||||
|
||||
public function addUserCreditItem($userBusiness)
|
||||
{
|
||||
//HTMLHelper::getMonth($userBusiness->month)
|
||||
|
||||
$date = $userBusiness->month.'#'.$userBusiness->year;
|
||||
{
|
||||
$date = HTMLHelper::getMonth($userBusiness->month).' '.$userBusiness->year;
|
||||
|
||||
if($userBusiness->commission_shop_sales > 0){
|
||||
if($this->hasNotUserCreditItem($userBusiness, 1)){
|
||||
|
|
@ -43,39 +41,22 @@ class UserPaymentCredits
|
|||
'user_id' => $userBusiness->user_id,
|
||||
'user_business_id' => $userBusiness->id,
|
||||
'credit' => $userBusiness->commission_shop_sales,
|
||||
'message' => 'payment.commission_shop#'.$date,
|
||||
'from_month' => $userBusiness->month,
|
||||
'from_year' => $userBusiness->year,
|
||||
'message' => 'Provision Shop '.$date,
|
||||
'status' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
if($userBusiness->commission_pp_total > 0){
|
||||
if($userBusiness->commission_team_total > 0){
|
||||
if($this->hasNotUserCreditItem($userBusiness, 2)){
|
||||
UserCreditItem::create([
|
||||
'user_id' => $userBusiness->user_id,
|
||||
'user_business_id' => $userBusiness->id,
|
||||
'credit' => $userBusiness->commission_pp_total,
|
||||
'message' => 'payment.commission_payline#'.$date,
|
||||
'from_month' => $userBusiness->month,
|
||||
'from_year' => $userBusiness->year,
|
||||
'credit' => $userBusiness->commission_team_total,
|
||||
'message' => 'Provision Team '.$date,
|
||||
'status' => 2,
|
||||
]);
|
||||
}
|
||||
}
|
||||
if($userBusiness->commission_growth_total > 0){
|
||||
if($this->hasNotUserCreditItem($userBusiness, 5)){
|
||||
UserCreditItem::create([
|
||||
'user_id' => $userBusiness->user_id,
|
||||
'user_business_id' => $userBusiness->id,
|
||||
'credit' => $userBusiness->commission_growth_total,
|
||||
'message' => 'payment.commission_growth_bonus#'.$date,
|
||||
'from_month' => $userBusiness->month,
|
||||
'from_year' => $userBusiness->year,
|
||||
'status' => 5,
|
||||
]);
|
||||
}
|
||||
}
|
||||
return $userBusiness;
|
||||
|
||||
}
|
||||
|
|
@ -93,7 +74,7 @@ class UserPaymentCredits
|
|||
$user = User::findOrFail($user_id);
|
||||
$data = [];
|
||||
if($credit_send_mail){
|
||||
$data['credit_send_mail'] = false;
|
||||
$data['credit_send_mail'] = true;
|
||||
}
|
||||
$credit_repo = new CreditRepository($user);
|
||||
return $credit_repo->create($data);
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domain;
|
||||
|
||||
/**
|
||||
* Early Domain Parser Service
|
||||
*
|
||||
* Provides domain parsing functionality that can be used during
|
||||
* bootstrap phase (RouteServiceProvider) and runtime (Middleware).
|
||||
*
|
||||
* This service caches parsing results per request to avoid duplicate work.
|
||||
*/
|
||||
class EarlyDomainParser
|
||||
{
|
||||
/**
|
||||
* Cache for parsed domain information per request
|
||||
* @var array|null
|
||||
*/
|
||||
private static ?array $cachedDomainInfo = null;
|
||||
|
||||
/**
|
||||
* Cache key (host) for cache invalidation
|
||||
* @var string|null
|
||||
*/
|
||||
private static ?string $cachedHost = null;
|
||||
|
||||
/**
|
||||
* Parse domain information from config/domains.php
|
||||
*
|
||||
* Results are cached per request to avoid duplicate parsing.
|
||||
*
|
||||
* @param string|null $host If null, uses HTTP_HOST or SERVER_NAME
|
||||
* @return array Domain information array
|
||||
*/
|
||||
public static function parseDomain(?string $host = null): array
|
||||
{
|
||||
// Get host from request if not provided
|
||||
if ($host === null) {
|
||||
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
|
||||
}
|
||||
|
||||
// Remove protocol if present
|
||||
$host = preg_replace('/^https?:\/\//', '', $host);
|
||||
|
||||
// Return cached result if available for same host
|
||||
if (self::$cachedHost === $host && self::$cachedDomainInfo !== null) {
|
||||
return self::$cachedDomainInfo;
|
||||
}
|
||||
// Load domains configuration
|
||||
$domains = self::getDomainsConfig();
|
||||
$reservedSubdomains = self::getReservedSubdomains();
|
||||
|
||||
// Check exact matches first (main, shop, crm, portal, checkout)
|
||||
foreach ($domains as $key => $domainConfig) {
|
||||
if ($key === 'user-shop') {
|
||||
continue; // Handle user-shop separately
|
||||
}
|
||||
|
||||
if ($host === $domainConfig['host']) {
|
||||
$domainInfo = [
|
||||
'type' => $domainConfig['type'],
|
||||
'host' => $host,
|
||||
'subdomain' => null,
|
||||
'config_key' => $key,
|
||||
];
|
||||
|
||||
// Cache the result for this request
|
||||
self::$cachedHost = $host;
|
||||
self::$cachedDomainInfo = $domainInfo;
|
||||
|
||||
return $domainInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for user-shop pattern (dynamic subdomains)
|
||||
if (isset($domains['user-shop'])) {
|
||||
$userShopPattern = $domains['user-shop']['host'];
|
||||
$baseDomain = str_replace('{subdomain}.', '', $userShopPattern);
|
||||
|
||||
if (str_ends_with($host, '.' . $baseDomain)) {
|
||||
$subdomain = str_replace('.' . $baseDomain, '', $host);
|
||||
|
||||
// Check if subdomain is not reserved
|
||||
if (!empty($subdomain) && !in_array($subdomain, $reservedSubdomains)) {
|
||||
$domainInfo = [
|
||||
'type' => 'user-shop',
|
||||
'host' => $host,
|
||||
'subdomain' => $subdomain,
|
||||
'config_key' => 'user-shop',
|
||||
];
|
||||
|
||||
// Cache the result for this request
|
||||
self::$cachedHost = $host;
|
||||
self::$cachedDomainInfo = $domainInfo;
|
||||
|
||||
return $domainInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown domain
|
||||
$domainInfo = [
|
||||
'type' => 'unknown',
|
||||
'host' => $host,
|
||||
'subdomain' => null,
|
||||
'config_key' => null,
|
||||
];
|
||||
|
||||
// Cache the result for this request
|
||||
self::$cachedHost = $host;
|
||||
self::$cachedDomainInfo = $domainInfo;
|
||||
|
||||
return $domainInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current domain type quickly (for RouteServiceProvider)
|
||||
*/
|
||||
public static function getCurrentDomainType(): string
|
||||
{
|
||||
$domainInfo = self::parseDomain();
|
||||
return $domainInfo['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current domain is a user shop
|
||||
*/
|
||||
public static function isUserShop(): bool
|
||||
{
|
||||
return self::getCurrentDomainType() === 'user-shop';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subdomain for user shops
|
||||
*/
|
||||
public static function getSubdomain(): ?string
|
||||
{
|
||||
$domainInfo = self::parseDomain();
|
||||
return $domainInfo['subdomain'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domains configuration (early bootstrap safe)
|
||||
*/
|
||||
private static function getDomainsConfig(): array
|
||||
{
|
||||
// Try Laravel config first (if available)
|
||||
if (function_exists('config')) {
|
||||
$config = config('domains.domains');
|
||||
if ($config) {
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Read config file directly
|
||||
$configPath = __DIR__ . '/../../../../config/domains.php';
|
||||
if (file_exists($configPath)) {
|
||||
$config = include $configPath;
|
||||
return $config['domains'] ?? [];
|
||||
}
|
||||
|
||||
// Last resort: Build from environment variables
|
||||
return self::buildConfigFromEnv();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reserved subdomains configuration
|
||||
*/
|
||||
private static function getReservedSubdomains(): array
|
||||
{
|
||||
// Try Laravel config first (if available)
|
||||
if (function_exists('config')) {
|
||||
$reserved = config('domains.reserved_subdomains');
|
||||
if ($reserved) {
|
||||
return $reserved;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Read config file directly
|
||||
$configPath = __DIR__ . '/../../../../config/domains.php';
|
||||
if (file_exists($configPath)) {
|
||||
$config = include $configPath;
|
||||
return $config['reserved_subdomains'] ?? [];
|
||||
}
|
||||
|
||||
// Default reserved subdomains
|
||||
return ['my', 'in', 'checkout', 'www', 'api', 'mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build basic domain configuration from environment variables
|
||||
* Used as fallback when config file is not available
|
||||
*/
|
||||
private static function buildConfigFromEnv(): array
|
||||
{
|
||||
$domain = $_ENV['APP_DOMAIN'] ?? 'mivita';
|
||||
$tldCare = $_ENV['APP_TLD_CARE'] ?? '.care';
|
||||
$tldShop = $_ENV['APP_TLD_SHOP'] ?? '.shop';
|
||||
$crmPrefix = $_ENV['APP_PRE_URL_CRM'] ?? 'my.';
|
||||
$portalPrefix = $_ENV['APP_PRE_URL_PORTAL'] ?? 'in.';
|
||||
$checkoutPrefix = $_ENV['APP_URL_CHECKOUT'] ?? 'checkout.';
|
||||
|
||||
return [
|
||||
'main' => [
|
||||
'host' => $domain . $tldCare,
|
||||
'type' => 'main',
|
||||
],
|
||||
'shop' => [
|
||||
'host' => $domain . $tldShop,
|
||||
'type' => 'main-shop',
|
||||
'default_user_shop' => 'aloevera',
|
||||
],
|
||||
'crm' => [
|
||||
'host' => $crmPrefix . $domain . $tldCare,
|
||||
'type' => 'crm',
|
||||
],
|
||||
'portal' => [
|
||||
'host' => $portalPrefix . $domain . $tldCare,
|
||||
'type' => 'portal',
|
||||
],
|
||||
'checkout' => [
|
||||
'host' => $checkoutPrefix . $domain . $tldCare,
|
||||
'type' => 'checkout',
|
||||
],
|
||||
'user-shop' => [
|
||||
'host' => '{subdomain}.' . $domain . $tldCare,
|
||||
'type' => 'user-shop',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get protocol from configuration
|
||||
*/
|
||||
public static function getProtocol(): string
|
||||
{
|
||||
if (function_exists('config')) {
|
||||
return config('domains.protocol', 'https://');
|
||||
}
|
||||
|
||||
return $_ENV['APP_PROTOCOL'] ?? 'https://';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main domain URL for redirects
|
||||
*/
|
||||
public static function getMainUrl(): string
|
||||
{
|
||||
$domains = self::getDomainsConfig();
|
||||
$mainHost = $domains['main']['host'] ?? 'localhost';
|
||||
$protocol = self::getProtocol();
|
||||
|
||||
return $protocol . $mainHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the internal cache (useful for testing or special cases)
|
||||
*/
|
||||
public static function clearCache(): void
|
||||
{
|
||||
self::$cachedDomainInfo = null;
|
||||
self::$cachedHost = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if result is cached for current/given host
|
||||
*/
|
||||
public static function isCached(?string $host = null): bool
|
||||
{
|
||||
if ($host === null) {
|
||||
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
|
||||
$host = preg_replace('/^https?:\/\//', '', $host);
|
||||
}
|
||||
|
||||
return self::$cachedHost === $host && self::$cachedDomainInfo !== null;
|
||||
}
|
||||
}
|
||||
71
app/Exceptions/Handler.php
Normal file → Executable file
71
app/Exceptions/Handler.php
Normal file → Executable file
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Symfony\Component\ErrorHandler\Exception\FlattenException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
|
|
@ -40,10 +36,6 @@ class Handler extends ExceptionHandler
|
|||
*/
|
||||
public function report(Throwable $exception)
|
||||
{
|
||||
|
||||
if ($this->shouldReport($exception)) {
|
||||
$this->sendEmail($exception);
|
||||
}
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
|
|
@ -60,65 +52,4 @@ class Handler extends ExceptionHandler
|
|||
{
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an authentication exception into a response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Auth\AuthenticationException $exception
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
protected function unauthenticated($request, \Illuminate\Auth\AuthenticationException $exception)
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['message' => $exception->getMessage()], 401);
|
||||
}
|
||||
|
||||
try {
|
||||
// HOTFIX: DomainContext temporär deaktiviert
|
||||
// TODO: Nach Claude v2 Implementation wieder aktivieren
|
||||
// $context = app(\App\Domain\DomainContext::class);
|
||||
// $loginRoute = match($context->type) {
|
||||
// 'portal' => 'portal.login.form',
|
||||
// 'crm' => 'login', // CRM hat eine eigene login route
|
||||
// default => 'login'
|
||||
// };
|
||||
|
||||
// Temporär: Verwende Standard-Login-Route
|
||||
return redirect()->guest(route('login'));
|
||||
} catch (\Exception $e) {
|
||||
// Fallback: Weiterleitung zur Hauptdomain
|
||||
return redirect()->guest('https://' . config('app.domain') . config('app.tld_care') . '/login');
|
||||
}
|
||||
}
|
||||
|
||||
public function sendEmail(Throwable $exception)
|
||||
{
|
||||
try {
|
||||
$e = FlattenException::create($exception);
|
||||
$handler = new HtmlErrorRenderer(true); // boolean, true raises debug flag...
|
||||
$css = $handler->getStylesheet();
|
||||
$content = $handler->getBody($e);
|
||||
//Mail::to(config('app.exception_mail'))->send(new MailContact($contact));
|
||||
// Verwende normale Mail-Klasse statt Facade, um Probleme bei der Initialisierung zu vermeiden
|
||||
$to = config('app.exception_mail');
|
||||
$subject = 'mivita Exception: ' . \Request::fullUrl();
|
||||
|
||||
if ($to) {
|
||||
\Mail::send('emails.exception', compact('css', 'content'), function ($message) use ($to, $subject) {
|
||||
$message
|
||||
->to($to)
|
||||
->subject($subject)
|
||||
;
|
||||
});
|
||||
}
|
||||
} catch (Throwable $ex) {
|
||||
// Einfache Fehlerprotokollierung ohne Facade
|
||||
file_put_contents(
|
||||
storage_path('logs/laravel-' . date('Y-m-d') . '.log'),
|
||||
'[' . date('Y-m-d H:i:s') . '] exception-handler-error: ' . $ex->getMessage() . "\n",
|
||||
FILE_APPEND
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
namespace App\Exports;
|
||||
|
||||
use Maatwebsite\Excel\Excel;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\Exportable;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
|
||||
class UserTeamExport implements FromCollection, WithHeadings
|
||||
{
|
||||
protected $collection;
|
||||
protected $headings;
|
||||
|
||||
use Exportable;
|
||||
|
||||
|
||||
public function __construct($data,$header)
|
||||
{
|
||||
$this->collection = $data;
|
||||
$this->headings = $header;
|
||||
}
|
||||
|
||||
public function collection()
|
||||
{
|
||||
return collect($this->collection);
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return [$this->headings];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
namespace App\Exports;
|
||||
|
||||
use Maatwebsite\Excel\Excel;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\Exportable;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
|
||||
class xExport implements FromCollection, WithHeadings
|
||||
{
|
||||
protected $collection;
|
||||
protected $headings;
|
||||
|
||||
use Exportable;
|
||||
|
||||
|
||||
public function __construct($data,$header)
|
||||
{
|
||||
$this->collection = $data;
|
||||
$this->headings = $header;
|
||||
}
|
||||
|
||||
public function collection()
|
||||
{
|
||||
return collect($this->collection);
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return [$this->headings];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\UserAbo;
|
||||
use App\Repositories\AboRepository;
|
||||
use App\Services\AboItemHistoryService;
|
||||
use App\Services\AboOrderCart;
|
||||
use App\Services\Shop;
|
||||
use Request;
|
||||
|
||||
class AboController extends Controller
|
||||
{
|
||||
protected $aboRepository;
|
||||
|
||||
public function __construct(AboRepository $aboRepository)
|
||||
{
|
||||
$this->middleware('admin');
|
||||
$this->aboRepository = $aboRepository;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if (Request::get('reset') === 'filter') {
|
||||
set_user_attr('filter_user_shop_id', null);
|
||||
set_user_attr('filter_status', null);
|
||||
set_user_attr('filter_member_id', null);
|
||||
|
||||
return redirect(route('admin_sales_customers'));
|
||||
}
|
||||
|
||||
// $filter_user_shops = UserAbo::join('user_shops', 'user_shop_id', '=', 'user_shops.id')->orderBy('slug')->get()->pluck('slug', 'id')->unique()->toArray();
|
||||
$filter_members = UserAbo::join('users', 'user_id', '=', 'users.id')->groupBy('user_id')->join('user_accounts', 'account_id', '=', 'user_accounts.id')->select('users.id', 'users.email', 'user_accounts.first_name', 'user_accounts.last_name')->get();
|
||||
|
||||
$data = [
|
||||
// 'filter_user_shops' => $filter_user_shops,
|
||||
'filter_members' => $filter_members,
|
||||
];
|
||||
|
||||
return view('admin.abo.index', $data);
|
||||
}
|
||||
|
||||
public function detail($id)
|
||||
{
|
||||
$data = Request::all();
|
||||
$user_abo = UserAbo::findOrFail($id);
|
||||
|
||||
// init Yard
|
||||
AboOrderCart::initYard($user_abo);
|
||||
$customer_detail = AboOrderCart::getCustomerDetail();
|
||||
AboOrderCart::makeOrderYard($user_abo);
|
||||
|
||||
$comp_products = [];
|
||||
if ($user_abo->is_for === 'me') {
|
||||
$comp_products = Shop::getCompProducts('abo-me');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'user_abo' => $user_abo,
|
||||
'isAdmin' => true,
|
||||
'customer_detail' => $customer_detail,
|
||||
'view' => $user_abo->is_for,
|
||||
'comp_products' => $comp_products,
|
||||
];
|
||||
|
||||
return view('admin.abo.detail', $data);
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
$data = Request::all();
|
||||
if (isset($data['action'])) {
|
||||
if ($data['action'] === 'abo_update_settings') {
|
||||
$user_abo = UserAbo::findOrFail($data['id']);
|
||||
$this->aboRepository->setModel($user_abo);
|
||||
$this->aboRepository->update($data);
|
||||
|
||||
return redirect(route('admin_abos_detail', [$id]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function rollback($id)
|
||||
{
|
||||
$user_abo = UserAbo::findOrFail($id);
|
||||
|
||||
AboOrderCart::initYard($user_abo);
|
||||
|
||||
$success = AboItemHistoryService::rollbackToInitial($user_abo);
|
||||
|
||||
if ($success) {
|
||||
$user_abo->refresh();
|
||||
AboOrderCart::makeOrderYard($user_abo);
|
||||
AboOrderCart::checkNumOfCompProducts($user_abo);
|
||||
|
||||
\Session()->flash('alert-success', __('abo_history.rollback_success'));
|
||||
} else {
|
||||
\Session()->flash('alert-error', __('abo_history.rollback_no_data'));
|
||||
}
|
||||
|
||||
return redirect(route('admin_abos_detail', [$id]));
|
||||
}
|
||||
|
||||
public function datatable()
|
||||
{
|
||||
|
||||
$query = UserAbo::with('user_abo_orders')->with('shopping_user')->select('user_abos.*');
|
||||
|
||||
set_user_attr('filter_member_id', Request::get('filter_member_id'));
|
||||
if (Request::get('filter_member_id') != '') {
|
||||
$query->where('user_id', '=', Request::get('filter_member_id'));
|
||||
}
|
||||
|
||||
set_user_attr('filter_status', Request::get('filter_status'));
|
||||
if (Request::get('filter_status') != '') {
|
||||
$query->where('status', '=', Request::get('filter_status'));
|
||||
}
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (UserAbo $user_abo) {
|
||||
return '<a href="'.route('admin_abos_detail', [$user_abo->id]).'" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
|
||||
})
|
||||
->addColumn('start_date', function (UserAbo $user_abo) {
|
||||
return $user_abo->start_date;
|
||||
})
|
||||
->addColumn('next_date', function (UserAbo $user_abo) {
|
||||
return $user_abo->next_date;
|
||||
})
|
||||
->addColumn('abo_interval', function (UserAbo $user_abo) {
|
||||
return \App\Services\HTMLHelper::getAboStrLang($user_abo->abo_interval);
|
||||
})
|
||||
->addColumn('status', function (UserAbo $user_abo) {
|
||||
return $user_abo->getStatusFormated();
|
||||
})
|
||||
->addColumn('active', function (UserAbo $user_abo) {
|
||||
return get_active_badge($user_abo->active);
|
||||
})
|
||||
|
||||
->addColumn('is_for', function (UserAbo $user_abo) {
|
||||
return $user_abo->getIsForFormated();
|
||||
})
|
||||
->addColumn('count', function (UserAbo $user_abo) {
|
||||
return $user_abo->getCountOrders();
|
||||
})
|
||||
->addColumn('amount', function (UserAbo $user_abo) {
|
||||
return $user_abo->getFormattedAmount().' €';
|
||||
})
|
||||
->addColumn('payment', function (UserAbo $user_abo) {
|
||||
return $user_abo->getPaymentType();
|
||||
})
|
||||
->addColumn('member', function (UserAbo $user_abo) {
|
||||
if (isset($user_abo->shopping_user) && $user_abo->shopping_user->member_id > 0) {
|
||||
return '<a href="'.route('admin_lead_edit', [$user_abo->shopping_user->member_id]).'">'.$user_abo->shopping_user->member->getFullName().'</a>';
|
||||
}
|
||||
})
|
||||
->addColumn('payone_userid', function (UserAbo $user_abo) {
|
||||
return $user_abo->payone_userid;
|
||||
})
|
||||
|
||||
->orderColumn('id', 'id $1')
|
||||
->orderColumn('start_date', 'start_date $1')
|
||||
->orderColumn('next_date', 'next_date $1')
|
||||
->orderColumn('abo_interval', 'abo_interval $1')
|
||||
->orderColumn('status', 'status $1')
|
||||
->orderColumn('active', 'active $1')
|
||||
->orderColumn('is_for', 'is_for $1')
|
||||
->orderColumn('count', 'count $1')
|
||||
->orderColumn('amount', 'amount $1')
|
||||
->orderColumn('payone_userid', 'payone_userid $1')
|
||||
->rawColumns(['id', 'status', 'active', 'is_for', 'member'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\DcCategory;
|
||||
use App\Models\DcFile;
|
||||
use App\Models\DcTag;
|
||||
use App\Repositories\DC\FileRepository;
|
||||
use App\Repositories\DC\TagRepository;
|
||||
use Request;
|
||||
use Response;
|
||||
use Util;
|
||||
|
||||
class DownloadController extends Controller
|
||||
{
|
||||
protected $tagRepository;
|
||||
|
||||
protected $fileRepository;
|
||||
|
||||
public function __construct(TagRepository $tagRepository, FileRepository $fileRepository)
|
||||
{
|
||||
$this->middleware('admin');
|
||||
$this->tagRepository = $tagRepository;
|
||||
$this->fileRepository = $fileRepository;
|
||||
}
|
||||
|
||||
public function files()
|
||||
{
|
||||
$q = DcFile::orderBy('id', 'desc')->get(); // File::all();
|
||||
$data = [
|
||||
'files' => $q,
|
||||
];
|
||||
|
||||
return view('admin.downloadcenter.files', $data);
|
||||
}
|
||||
|
||||
public function fileEdit($id = null)
|
||||
{
|
||||
$file = $id ? DcFile::find($id) : new DcFile;
|
||||
$data = [
|
||||
'file' => $file,
|
||||
'categories' => DcCategory::where('active', true)->orderBy('pos')->get(),
|
||||
'tags' => DcTag::orderBy('pos')->get(),
|
||||
];
|
||||
|
||||
return view('admin.downloadcenter.file_edit', $data);
|
||||
}
|
||||
|
||||
public function fileUpdate($do, $id)
|
||||
{
|
||||
|
||||
if ($do === 'make_thumb') {
|
||||
$this->fileRepository->makeThumb($id);
|
||||
\Session()->flash('alert-success', 'Vorschaubild erstellt!');
|
||||
|
||||
return back();
|
||||
}
|
||||
if ($do === 'delete') {
|
||||
$this->fileRepository->deleteFile($id);
|
||||
\Session()->flash('alert-success', 'Datei gelöscht!');
|
||||
|
||||
return redirect(route('admin_downloadcenter_files'));
|
||||
}
|
||||
if ($do === 'delete_thumb') {
|
||||
$this->fileRepository->deleteThumb($id);
|
||||
\Session()->flash('alert-success', 'Vorschaubild gelöscht!');
|
||||
|
||||
return back();
|
||||
}
|
||||
if ($do === 'deactivate') {
|
||||
$file = DcFile::findOrFail($id);
|
||||
$file->active = false;
|
||||
$file->save();
|
||||
\Session()->flash('alert-success', 'Datei nicht anzeigen!');
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
if ($do === 'activate') {
|
||||
$file = DcFile::findOrFail($id);
|
||||
$file->active = true;
|
||||
$file->save();
|
||||
\Session()->flash('alert-success', 'Datei wird angezeigt!');
|
||||
|
||||
return back();
|
||||
}
|
||||
if ($do === 'file_tags_update') {
|
||||
$file = DcFile::findOrFail($id);
|
||||
$tags = Request::get('nestable_check', []);
|
||||
$this->fileRepository->tagsUpdate($id, is_array($tags) ? $tags : []);
|
||||
\Session()->flash('alert-success', 'Tags aktualisiert!');
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
|
||||
public function upload()
|
||||
{
|
||||
return view('admin.downloadcenter.file_upload');
|
||||
}
|
||||
|
||||
public function uploadFile()
|
||||
{
|
||||
$data = Request::all();
|
||||
$file = $this->fileRepository->uploadFile($data);
|
||||
|
||||
return Response::json([
|
||||
'error' => false,
|
||||
'filename' => $file->filename,
|
||||
'filedata' => '',
|
||||
'code' => 200,
|
||||
], 200);
|
||||
|
||||
// return response()->json(['success'=>basename($file)]);
|
||||
}
|
||||
|
||||
public function tags($flash = false)
|
||||
{
|
||||
|
||||
$active = DcCategory::orderBy('pos')->get();
|
||||
$inactive = DcTag::where('category_id', null)->get();
|
||||
$data = [
|
||||
'category_active' => $active,
|
||||
'tags_inactive' => $inactive,
|
||||
];
|
||||
if ($flash) {
|
||||
\Session()->flash('alert-success', 'gespeichert!');
|
||||
}
|
||||
|
||||
return view('admin.downloadcenter.tags', $data);
|
||||
}
|
||||
|
||||
public function storeItem($obj = false)
|
||||
{
|
||||
$data = Request::all();
|
||||
|
||||
return $this->tagRepository->storeItem($obj, $data);
|
||||
|
||||
return redirect(route('admin_downloadcenter_tags'));
|
||||
}
|
||||
|
||||
public function deleteItem($obj, $id)
|
||||
{
|
||||
$this->tagRepository->deleteItem($obj, $id);
|
||||
|
||||
return redirect(route('admin_downloadcenter_tags'));
|
||||
}
|
||||
|
||||
public function datatable()
|
||||
{
|
||||
|
||||
$query = DcFile::with('tags')->select('dc_files.*');
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (DcFile $file) {
|
||||
return '<a href="'.route('admin_downloadcenter_file_edit', [$file->id]).'" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
|
||||
})
|
||||
->addColumn('image', function (DcFile $file) {
|
||||
return ($file->hasThumb() && $file->hasBig()) ?
|
||||
'<img src="'.route('storage_file', [$file->id, 'dc_thumb', 'image']).'" class="img-fluid img-responsive" style="max-width: 100px;">' :
|
||||
'<a href="'.route('admin_downloadcenter_file', ['make_thumb', $file->id]).'" class="btn btn-sm btn-warning"> Vorschaubild<br>erstellen <i class="ion ion-md-refresh-circle"></i></a>';
|
||||
})
|
||||
->addColumn('name', function (DcFile $file) {
|
||||
// Storage::disk('local')->url($file->filename) }}
|
||||
return '<a target="_blank" href="'.route('storage_file', [$file->id, 'dc_file', 'stream']).'">'.$file->original_name.'</a>';
|
||||
// return '<a target="_blank" href="">'.$file->original_name.'</a>';
|
||||
})
|
||||
->addColumn('category', function (DcFile $file) {
|
||||
// return $file->category ? $file->category->name : '';
|
||||
})
|
||||
->addColumn('tags', function (DcFile $file) {
|
||||
// return $file->hasTags() ? '<span class="badge badge-pill badge-success">('.$file->fileTag()->count().')</span>' : '<span class="badge badge-pill badge-dange">X</span>';
|
||||
return $file->tags->implode('name', '<br>');
|
||||
})
|
||||
->addColumn('size', function (DcFile $file) {
|
||||
return Util::formatBytes($file->size);
|
||||
})
|
||||
->addColumn('active', function (DcFile $file) {
|
||||
return get_active_badge($file->active);
|
||||
// return $file->active ? '<span class="badge badge-pill badge-success"><i class="fa fa-check-circle"></i> aktiv</span>' : '<span class="badge badge-pill badge-danger"><i class="fa fa-times-circle"></i> inaktiv</span>';
|
||||
})
|
||||
->addColumn('created_at', function (DcFile $file) {
|
||||
return $file->created_at->format('d.m.Y H:i');
|
||||
})
|
||||
->addColumn('updated_at', function (DcFile $file) {
|
||||
return $file->updated_at->format('d.m.Y');
|
||||
})
|
||||
->addColumn('action', function (DcFile $file) {
|
||||
return '<a onclick="return confirm(\'Diese Datei wirklich löschen?\');" class="btn btn-sm btn-danger" href="'.route('admin_downloadcenter_file', ['delete', $file->id]).'"><i class="fa fa-trash"></i></a>';
|
||||
})
|
||||
->filterColumn('name', function ($query, $keyword) {
|
||||
if ($keyword != '') {
|
||||
$query->where('original_name', 'LIKE', '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->orderColumn('id', 'id $1')
|
||||
->orderColumn('name', 'original_name $1')
|
||||
->orderColumn('original_name', 'original_name $1')
|
||||
->orderColumn('category', 'category $1')
|
||||
->orderColumn('size', 'size $1')
|
||||
->orderColumn('active', 'active $1')
|
||||
->orderColumn('created_at', 'created_at $1')
|
||||
->rawColumns(['id', 'image', 'name', 'active', 'tags', 'action'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,411 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Incentive;
|
||||
use App\Models\IncentiveNewAbo;
|
||||
use App\Models\IncentiveNewPartner;
|
||||
use App\Models\IncentiveParticipant;
|
||||
use App\Models\IncentivePointsLog;
|
||||
use App\Models\UserAboOrder;
|
||||
use App\Models\UserSalesVolume;
|
||||
use App\Services\Incentive\IncentiveCalculationService;
|
||||
use App\User;
|
||||
use Request;
|
||||
|
||||
class IncentiveController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('admin');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
return view('admin.incentive.index');
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return view('admin.incentive.create', [
|
||||
'languages' => config('localization.supportedLocales'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
$data = Request::validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'subtitle' => 'nullable|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'image' => 'nullable|string|max:255',
|
||||
'terms' => 'nullable|string',
|
||||
'qualification_start' => 'required|date',
|
||||
'qualification_end' => 'required|date|after_or_equal:qualification_start',
|
||||
'calculation_end' => 'required|date|after_or_equal:qualification_end',
|
||||
'points_partner_onetime' => 'required|integer|min:0',
|
||||
'points_abo_onetime' => 'required|integer|min:0',
|
||||
'min_direct_partners' => 'required|integer|min:0',
|
||||
'min_customer_abos' => 'required|integer|min:0',
|
||||
'max_winners' => 'required|integer|min:1',
|
||||
'status' => 'required|integer|in:0,1,2',
|
||||
]);
|
||||
|
||||
$data = array_merge($data, $this->extractTranslations());
|
||||
|
||||
Incentive::create($data);
|
||||
|
||||
\Session()->flash('alert-success', __('incentive.created'));
|
||||
|
||||
return redirect(route('admin_incentives'));
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$incentive = Incentive::findOrFail($id);
|
||||
$participants = IncentiveParticipant::where('incentive_id', $incentive->id)
|
||||
->with('user', 'user.account')
|
||||
->orderByIncentiveLeaderboard()
|
||||
->get();
|
||||
|
||||
return view('admin.incentive.show', [
|
||||
'incentive' => $incentive,
|
||||
'participants' => $participants,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$incentive = Incentive::findOrFail($id);
|
||||
|
||||
return view('admin.incentive.edit', [
|
||||
'incentive' => $incentive,
|
||||
'languages' => config('localization.supportedLocales'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
$data = Request::validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'subtitle' => 'nullable|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'image' => 'nullable|string|max:255',
|
||||
'terms' => 'nullable|string',
|
||||
'qualification_start' => 'required|date',
|
||||
'qualification_end' => 'required|date|after_or_equal:qualification_start',
|
||||
'calculation_end' => 'required|date|after_or_equal:qualification_end',
|
||||
'points_partner_onetime' => 'required|integer|min:0',
|
||||
'points_abo_onetime' => 'required|integer|min:0',
|
||||
'min_direct_partners' => 'required|integer|min:0',
|
||||
'min_customer_abos' => 'required|integer|min:0',
|
||||
'max_winners' => 'required|integer|min:1',
|
||||
'status' => 'required|integer|in:0,1,2',
|
||||
]);
|
||||
|
||||
$data = array_merge($data, $this->extractTranslations());
|
||||
|
||||
$incentive = Incentive::findOrFail($id);
|
||||
$incentive->update($data);
|
||||
|
||||
\Session()->flash('alert-success', __('incentive.updated'));
|
||||
|
||||
return redirect(route('admin_incentive_show', [$id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{trans_name: array<string, string>, trans_description: array<string, string>, trans_terms: array<string, string>}
|
||||
*/
|
||||
private function extractTranslations(): array
|
||||
{
|
||||
$transName = [];
|
||||
$transDescription = [];
|
||||
$transTerms = [];
|
||||
|
||||
$transSubtitle = [];
|
||||
|
||||
foreach (config('localization.supportedLocales') as $locale => $localeData) {
|
||||
if ($locale !== 'de') {
|
||||
$transName[$locale] = Request::get('trans_name_'.$locale, '');
|
||||
$transSubtitle[$locale] = Request::get('trans_subtitle_'.$locale, '');
|
||||
$transDescription[$locale] = Request::get('trans_description_'.$locale, '');
|
||||
$transTerms[$locale] = Request::get('trans_terms_'.$locale, '');
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'trans_name' => $transName,
|
||||
'trans_subtitle' => $transSubtitle,
|
||||
'trans_description' => $transDescription,
|
||||
'trans_terms' => $transTerms,
|
||||
];
|
||||
}
|
||||
|
||||
public function recalculate($id)
|
||||
{
|
||||
$incentive = Incentive::findOrFail($id);
|
||||
$service = new IncentiveCalculationService;
|
||||
$stats = $service->recalculate($incentive, Request::has('force'));
|
||||
|
||||
\Session()->flash('alert-success', __('incentive.recalculated', [
|
||||
'participants' => $stats['participants'],
|
||||
'errors' => $stats['errors'],
|
||||
]));
|
||||
|
||||
return redirect(route('admin_incentive_show', [$id]));
|
||||
}
|
||||
|
||||
public function participantDetails($participant_id)
|
||||
{
|
||||
$participant = IncentiveParticipant::with('incentive', 'user', 'user.account')
|
||||
->findOrFail($participant_id);
|
||||
|
||||
$data = self::buildParticipantDetailData($participant);
|
||||
|
||||
return view('admin.incentive._participant_details', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Baut die Detail-Daten fuer einen Teilnehmer auf.
|
||||
* Wird von Admin und User Controller genutzt.
|
||||
*/
|
||||
public static function buildParticipantDetailData(IncentiveParticipant $participant): array
|
||||
{
|
||||
$incentive = $participant->incentive;
|
||||
$calculation_months = $incentive->getCalculationMonths();
|
||||
|
||||
// Alle Logs dieses Teilnehmers (ohne Stornos)
|
||||
$all_logs = IncentivePointsLog::where('participant_id', $participant->id)
|
||||
->where('is_storno', false)
|
||||
->with('salesVolume')
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
// UserSalesVolume-IDs -> User-ID Mapping aufbauen (fuer akkumulierte Partner-Punkte)
|
||||
$sv_ids = $all_logs->whereNotNull('user_sales_volume_id')->pluck('user_sales_volume_id')->unique()->toArray();
|
||||
$sv_user_map = ! empty($sv_ids)
|
||||
? UserSalesVolume::whereIn('id', $sv_ids)->pluck('user_id', 'id')->toArray()
|
||||
: [];
|
||||
|
||||
// Partner aus Tracking-Tabelle
|
||||
$new_partners = IncentiveNewPartner::where('participant_id', $participant->id)
|
||||
->with('user', 'user.account')
|
||||
->orderBy('registered_at')
|
||||
->get();
|
||||
|
||||
$partner_logs = $all_logs->where('type', 'partner');
|
||||
|
||||
$partner_sources = $new_partners->map(function ($np) use ($partner_logs, $sv_user_map, $calculation_months, $incentive) {
|
||||
$monthly = [];
|
||||
$transactions = [];
|
||||
|
||||
foreach ($calculation_months as $period) {
|
||||
// Akkumulierte Logs: source_type=UserSalesVolume, deren USV.user_id == partner user_id
|
||||
$month_logs = $partner_logs
|
||||
->where('month', $period['month'])
|
||||
->where('year', $period['year'])
|
||||
->filter(function ($log) use ($np, $sv_user_map) {
|
||||
if ($log->source_type === User::class) {
|
||||
return false; // Einmal-Punkte nicht in Monatsspalte
|
||||
}
|
||||
|
||||
if ($log->incentive_new_partner_id) {
|
||||
return (int) $log->incentive_new_partner_id === (int) $np->id;
|
||||
}
|
||||
|
||||
// Legacy: USV.user_id muss zum Partner gehoeren
|
||||
return isset($sv_user_map[$log->user_sales_volume_id])
|
||||
&& $sv_user_map[$log->user_sales_volume_id] === $np->user_id;
|
||||
});
|
||||
|
||||
$month_points = (int) $month_logs->sum('points_accumulated');
|
||||
$monthly[] = $month_points;
|
||||
|
||||
foreach ($month_logs as $log) {
|
||||
$transactions[] = [
|
||||
'date' => $log->created_at->format('d.m.Y'),
|
||||
'month' => $period['month'],
|
||||
'year' => $period['year'],
|
||||
'label' => $log->source_label ?: 'KP #'.($log->user_sales_volume_id ?? $log->source_id),
|
||||
'points' => $log->points_accumulated,
|
||||
'type' => 'accumulated',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Einmal-Punkte als Transaktion hinzufuegen
|
||||
$onetime_log = $partner_logs
|
||||
->where('source_type', User::class)
|
||||
->first(function ($log) use ($np) {
|
||||
if ($log->incentive_new_partner_id) {
|
||||
return (int) $log->incentive_new_partner_id === (int) $np->id;
|
||||
}
|
||||
|
||||
return (int) $log->source_id === (int) $np->user_id;
|
||||
});
|
||||
|
||||
if ($onetime_log) {
|
||||
array_unshift($transactions, [
|
||||
'date' => $onetime_log->created_at->format('d.m.Y'),
|
||||
'month' => $onetime_log->month,
|
||||
'year' => $onetime_log->year,
|
||||
'label' => __('incentive.onetime_registration'),
|
||||
'points' => $onetime_log->points_onetime,
|
||||
'type' => 'onetime',
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $np->id,
|
||||
'label' => $np->user ? ($np->user->getFullName() ?: $np->user->email ?: 'User #'.$np->user_id) : 'User #'.$np->user_id,
|
||||
'month' => $np->registered_at->month,
|
||||
'year' => $np->registered_at->year,
|
||||
'onetime' => $incentive->points_partner_onetime,
|
||||
'monthly' => $monthly,
|
||||
'total' => $incentive->points_partner_onetime + array_sum($monthly),
|
||||
'transactions' => $transactions,
|
||||
];
|
||||
});
|
||||
|
||||
// Abos aus Tracking-Tabelle
|
||||
$new_abos = IncentiveNewAbo::where('participant_id', $participant->id)
|
||||
->with('userAbo')
|
||||
->orderBy('activated_at')
|
||||
->get();
|
||||
|
||||
$abo_logs = $all_logs->where('type', 'abo');
|
||||
|
||||
// Legacy-Fallback: USV -> user_abo_id (Logs ohne incentive_new_abo_id)
|
||||
$sv_user_abo_map = [];
|
||||
$needs_legacy_abo_map = $abo_logs->whereNull('incentive_new_abo_id')->whereNotNull('user_sales_volume_id')->isNotEmpty();
|
||||
if ($needs_legacy_abo_map && ! empty($sv_ids)) {
|
||||
$sv_rows = UserSalesVolume::query()
|
||||
->whereIn('id', $sv_ids)
|
||||
->whereNotNull('shopping_order_id')
|
||||
->get(['id', 'shopping_order_id']);
|
||||
$order_ids = $sv_rows->pluck('shopping_order_id')->unique()->filter()->values();
|
||||
if ($order_ids->isNotEmpty()) {
|
||||
$user_abo_id_by_order_id = UserAboOrder::query()
|
||||
->whereIn('shopping_order_id', $order_ids)
|
||||
->get(['shopping_order_id', 'user_abo_id'])
|
||||
->keyBy('shopping_order_id');
|
||||
foreach ($sv_rows as $sv) {
|
||||
$link = $user_abo_id_by_order_id->get($sv->shopping_order_id);
|
||||
if ($link) {
|
||||
$sv_user_abo_map[$sv->id] = (int) $link->user_abo_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$abo_sources = $new_abos->map(function ($na) use ($abo_logs, $sv_user_abo_map, $calculation_months, $incentive) {
|
||||
$monthly = [];
|
||||
$transactions = [];
|
||||
$tracked_user_abo_id = (int) $na->user_abo_id;
|
||||
|
||||
foreach ($calculation_months as $period) {
|
||||
$month_logs = $abo_logs
|
||||
->where('month', $period['month'])
|
||||
->where('year', $period['year'])
|
||||
->filter(function ($log) use ($na, $tracked_user_abo_id, $sv_user_abo_map) {
|
||||
if ($log->source_type !== UserSalesVolume::class) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($log->incentive_new_abo_id) {
|
||||
return (int) $log->incentive_new_abo_id === (int) $na->id;
|
||||
}
|
||||
|
||||
$usv_id = $log->user_sales_volume_id;
|
||||
if (! $usv_id || ! isset($sv_user_abo_map[$usv_id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $sv_user_abo_map[$usv_id] === $tracked_user_abo_id;
|
||||
});
|
||||
|
||||
$month_points = (int) $month_logs->sum('points_accumulated');
|
||||
$monthly[] = $month_points;
|
||||
|
||||
foreach ($month_logs as $log) {
|
||||
$transactions[] = [
|
||||
'date' => $log->created_at->format('d.m.Y'),
|
||||
'month' => $period['month'],
|
||||
'year' => $period['year'],
|
||||
'label' => $log->source_label ?: 'SV #'.($log->user_sales_volume_id ?? $log->source_id),
|
||||
'points' => $log->points_accumulated,
|
||||
'type' => 'accumulated',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Einmal-Punkte als Transaktion
|
||||
$onetime_log = $abo_logs
|
||||
->where('source_type', '!=', UserSalesVolume::class)
|
||||
->first(function ($log) use ($na) {
|
||||
if ($log->incentive_new_abo_id) {
|
||||
return (int) $log->incentive_new_abo_id === (int) $na->id;
|
||||
}
|
||||
|
||||
return (int) $log->source_id === (int) $na->user_abo_id;
|
||||
});
|
||||
|
||||
if ($onetime_log) {
|
||||
array_unshift($transactions, [
|
||||
'date' => $onetime_log->created_at->format('d.m.Y'),
|
||||
'month' => $onetime_log->month,
|
||||
'year' => $onetime_log->year,
|
||||
'label' => __('incentive.onetime_abo_activation'),
|
||||
'points' => $onetime_log->points_onetime,
|
||||
'type' => 'onetime',
|
||||
]);
|
||||
}
|
||||
|
||||
$label = $na->userAbo?->email ?: ('Abo #'.$na->user_abo_id);
|
||||
|
||||
return [
|
||||
'id' => $na->id,
|
||||
'label' => $label,
|
||||
'month' => $na->activated_at->month,
|
||||
'year' => $na->activated_at->year,
|
||||
'onetime' => $incentive->points_abo_onetime,
|
||||
'monthly' => $monthly,
|
||||
'total' => $incentive->points_abo_onetime + array_sum($monthly),
|
||||
'transactions' => $transactions,
|
||||
];
|
||||
});
|
||||
|
||||
return [
|
||||
'incentive' => $incentive,
|
||||
'participant' => $participant,
|
||||
'calculation_months' => $calculation_months,
|
||||
'partner_sources' => $partner_sources,
|
||||
'abo_sources' => $abo_sources,
|
||||
];
|
||||
}
|
||||
|
||||
public function datatable()
|
||||
{
|
||||
$query = Incentive::query()->select('incentives.*');
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('action', function (Incentive $incentive) {
|
||||
return '<a href="'.route('admin_incentive_show', [$incentive->id]).'" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-eye"></span></a>
|
||||
<a href="'.route('admin_incentive_edit', [$incentive->id]).'" class="btn icon-btn btn-sm btn-warning"><span class="fa fa-edit"></span></a>';
|
||||
})
|
||||
->addColumn('status_label', function (Incentive $incentive) {
|
||||
return '<span class="badge badge-'.$incentive->getStatusColor().'">'.$incentive->getStatusType().'</span>';
|
||||
})
|
||||
->addColumn('period', function (Incentive $incentive) {
|
||||
return $incentive->qualification_start->format('d.m.Y').' - '.$incentive->qualification_end->format('d.m.Y');
|
||||
})
|
||||
->addColumn('participants_count', function (Incentive $incentive) {
|
||||
return $incentive->participants()->count();
|
||||
})
|
||||
->orderColumn('name', 'name $1')
|
||||
->orderColumn('status_label', 'status $1')
|
||||
->rawColumns(['action', 'status_label'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,467 +0,0 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
use Auth;
|
||||
use Request;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use App\Exports\xExport;
|
||||
use App\Models\UserInvoice;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Exports\UserTeamExport;
|
||||
use App\Models\ShoppingOrderItem;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use App\Services\BusinessPlan\ExportBot;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class PaymentSalesController extends Controller
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('admin');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->setFilterVars();
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_years' => HTMLHelper::getYearRange(2022),
|
||||
];
|
||||
return view('admin.payment.salesvolume', $data);
|
||||
}
|
||||
|
||||
|
||||
public function download(){
|
||||
|
||||
/*
|
||||
EXCEL EXPORT function */
|
||||
|
||||
/*
|
||||
if(Request::get('action') === "exportfull_paid"){
|
||||
return $this->exportFullList(1);
|
||||
}
|
||||
if(Request::get('action') === "exportfull_unpaid"){
|
||||
return $this->exportFullList(0);
|
||||
}
|
||||
*/
|
||||
if(Request::get('action') === "exportfull_paid_invoice"){
|
||||
return $this->exportFullListInvoice();
|
||||
}
|
||||
if(Request::get('action') === "export"){
|
||||
return $this->exportKompaktListInvoice();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function setFilterVars(){
|
||||
|
||||
if(!session('payment_sales_vol_filter_month')){
|
||||
session(['payment_sales_vol_filter_month' => intval(date('m'))]);
|
||||
}
|
||||
if(!session('payment_sales_vol_filter_year')){
|
||||
session(['payment_sales_vol_filter_year' => intval(date('Y'))]);
|
||||
}
|
||||
|
||||
if(Request::get('payment_sales_vol_filter_month')){
|
||||
session(['payment_sales_vol_filter_month' => Request::get('payment_sales_vol_filter_month')]);
|
||||
}
|
||||
if(Request::get('payment_sales_vol_filter_year')){
|
||||
session(['payment_sales_vol_filter_year' => Request::get('payment_sales_vol_filter_year')]);
|
||||
}
|
||||
}
|
||||
|
||||
private function exportKompaktListInvoice(){
|
||||
$objects = $this->initKompaktList();
|
||||
$columns = [];
|
||||
$filename = "mivita-absatzmengen-kompakt".session('payment_sales_vol_filter_month').'_'.session('payment_sales_vol_filter_year')."-export";
|
||||
$headers = array(
|
||||
'#',
|
||||
'Produkt',
|
||||
'Artikelnummer',
|
||||
'Menge',
|
||||
|
||||
);
|
||||
if($objects){
|
||||
foreach ($objects as $key => $obj){
|
||||
$columns[] = array(
|
||||
'id' => $key,
|
||||
'name' => $obj['name'],
|
||||
'number' => $obj['number'],
|
||||
'value' => $obj['value'],
|
||||
);
|
||||
}
|
||||
}
|
||||
return Excel::download(new UserTeamExport($columns, $headers), $filename.'.xls');
|
||||
}
|
||||
|
||||
|
||||
private function exportFullListInvoice(){
|
||||
|
||||
$this->setFilterVars();
|
||||
|
||||
$UserInvoices = UserInvoice::with('shopping_order')->with('shopping_order.shopping_user')->select('user_invoices.*')
|
||||
->where('user_invoices.month', '=', session('payment_sales_vol_filter_month'))
|
||||
->where('user_invoices.year', '=', session('payment_sales_vol_filter_year'))
|
||||
->get();
|
||||
|
||||
$headers = array('Rechnungsnummer','Datum', 'EMail', 'Zahlung', 'ProduktNummer', 'ProduktName', 'Anzahl', 'Summe', 'Kompensation');
|
||||
$columns = [];
|
||||
$hasSOID = [];
|
||||
$total_value = [];
|
||||
|
||||
foreach($UserInvoices as $UserInvoice){
|
||||
if($UserInvoice->shopping_order){
|
||||
$ShoppingOrder = $UserInvoice->shopping_order;
|
||||
$object = [];
|
||||
$object['Rechnungsnummer'] = $UserInvoice->full_number;
|
||||
$object['Datum'] = $UserInvoice->date;
|
||||
$object['EMail'] = $ShoppingOrder->shopping_user ? $ShoppingOrder->shopping_user->billing_email : 'n/a';
|
||||
$object['Zahlung'] = $ShoppingOrder->getPaymentForType();
|
||||
|
||||
if($ShoppingOrder->payment_for === 5){ //homeparty
|
||||
if($ShoppingOrder->homeparty){
|
||||
foreach($ShoppingOrder->homeparty->homeparty_order_items as $homeparty_item){
|
||||
$total_value[$homeparty_item->product_id] = isset($total_value[$homeparty_item->product_id]) ? $total_value[$homeparty_item->product_id] + $homeparty_item->qty : $homeparty_item->qty;
|
||||
$object['ProduktNummer'] = $homeparty_item->product ? $homeparty_item->product->number : "n/a";
|
||||
$object['ProduktName'] = $homeparty_item->product ? $homeparty_item->product->name : "n/a";
|
||||
$object['Anzahl'] = $homeparty_item->qty;
|
||||
$object['Summe'] = $total_value[$homeparty_item->product_id];
|
||||
$object['Kompensation'] = '';
|
||||
$columns[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
}elseif($ShoppingOrder->payment_for === 8){ //collective_invoice
|
||||
if($ShoppingOrder->shopping_collect_order){
|
||||
foreach($ShoppingOrder->shopping_collect_order->shop_items as $shop_item){
|
||||
$total_value[$shop_item['pid']] = isset($total_value[$shop_item['pid']]) ? $total_value[$shop_item['pid']] + $shop_item['qty'] : $shop_item['qty'];
|
||||
$object['ProduktNummer'] = $shop_item['number'];
|
||||
$object['ProduktName'] = $shop_item['name'];
|
||||
$object['Anzahl'] = $shop_item['qty'];
|
||||
$object['Summe'] = $total_value[$shop_item['pid']];
|
||||
$object['Kompensation'] = '';
|
||||
$columns[] = $object;
|
||||
}
|
||||
|
||||
}
|
||||
}else{
|
||||
if($ShoppingOrder->shopping_order_items){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
$total_value[$shopping_order_item->product_id] = isset($total_value[$shopping_order_item->product_id]) ? $total_value[$shopping_order_item->product_id] + $shopping_order_item->qty : $shopping_order_item->qty;
|
||||
$object['ProduktNummer'] = $shopping_order_item->product ? $shopping_order_item->product->number : "n/a";
|
||||
$object['ProduktName'] = $shopping_order_item->product ? $shopping_order_item->product->name : "n/a";
|
||||
$object['Anzahl'] = $shopping_order_item->qty;
|
||||
$object['Summe'] = $total_value[$shopping_order_item->product_id];
|
||||
$object['Kompensation'] = ($shopping_order_item->comp ? $shopping_order_item->comp : '');
|
||||
|
||||
$columns[] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
$hasSOID[] = $ShoppingOrder->id;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$filename = "mivita-absatzmengen-voll-".session('payment_sales_vol_filter_month').'_'.session('payment_sales_vol_filter_year')."-export";
|
||||
return Excel::download(new xExport($columns, $headers), $filename.'.xls');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function initKompaktList()
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
$UserInvoices = UserInvoice::with('shopping_order')->with('shopping_order.shopping_user')->select('user_invoices.*')
|
||||
->where('user_invoices.month', '=', session('payment_sales_vol_filter_month'))
|
||||
->where('user_invoices.year', '=', session('payment_sales_vol_filter_year'))
|
||||
->get();
|
||||
|
||||
$objects = [];
|
||||
|
||||
foreach($UserInvoices as $UserInvoice){
|
||||
if($UserInvoice->shopping_order){
|
||||
$ShoppingOrder = $UserInvoice->shopping_order;
|
||||
|
||||
if($ShoppingOrder->payment_for === 5){ //homeparty
|
||||
if($ShoppingOrder->homeparty){
|
||||
foreach($ShoppingOrder->homeparty->homeparty_order_items as $homeparty_item){
|
||||
if(isset($objects[$homeparty_item->product_id])){
|
||||
$value = intval($objects[$homeparty_item->product_id]['value'] + $homeparty_item->qty);
|
||||
$objects[$homeparty_item->product_id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$homeparty_item->product_id] = [
|
||||
'name' => $homeparty_item->product ? $homeparty_item->product->name : "n/a ".$homeparty_item->product_id,
|
||||
'number' => $homeparty_item->product ? $homeparty_item->product->number : "n/a ".$homeparty_item->product_id,
|
||||
'value' => $homeparty_item->qty
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}elseif($ShoppingOrder->payment_for === 8){ //collective_invoice
|
||||
if($ShoppingOrder->shopping_collect_order){
|
||||
foreach($ShoppingOrder->shopping_collect_order->shop_items as $shop_item){
|
||||
|
||||
if(isset($objects[$shop_item['pid']])){
|
||||
$value = intval($objects[$shop_item['pid']]['value'] + $shop_item['qty']);
|
||||
$objects[$shop_item['pid']]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shop_item['pid']] = [
|
||||
'name' => $shop_item['name'],
|
||||
'number' => $shop_item['number'],
|
||||
'value' => $shop_item['qty']
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if($ShoppingOrder->shopping_order_items){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
if(isset($objects[$shopping_order_item->product_id])){
|
||||
$value = intval($objects[$shopping_order_item->product_id]['value'] + $shopping_order_item->qty);
|
||||
$objects[$shopping_order_item->product_id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shopping_order_item->product_id] = [
|
||||
'name' => $shopping_order_item->product ? $shopping_order_item->product->name : "n/a ".$shopping_order_item->product_id,
|
||||
'number' => $shopping_order_item->product ? $shopping_order_item->product->number : "n/a ".$shopping_order_item->product_id,
|
||||
'value' => $shopping_order_item->qty
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$hasSOID[] = $ShoppingOrder->id;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $objects;
|
||||
|
||||
}
|
||||
|
||||
public function datatable(){
|
||||
|
||||
|
||||
/*$collect = collect([
|
||||
['id' => 1, 'name' => 'John', 'number'=>92012, 'value'=>123],
|
||||
['id' => 2, 'name' => 'Jane', 'number'=>92012, 'value'=>123],
|
||||
['id' => 3, 'name' => 'James', 'number'=>92012, 'value'=>123],
|
||||
]);*/
|
||||
$objects = $this->initKompaktList();
|
||||
$collection = collect();
|
||||
if($objects){
|
||||
foreach($objects as $key => $obj){
|
||||
$collection->push([
|
||||
'id' => $key,
|
||||
'name' => $obj['name'],
|
||||
'number' => $obj['number'],
|
||||
'value' => $obj['value'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return \DataTables::of($collection)->toJson();
|
||||
}
|
||||
|
||||
/*
|
||||
//Auswertung nach ShoppingOrder
|
||||
//nach Datum created_at wann die Bestellung erstellt wurde
|
||||
//Ist nicht das Datum der Rechnung, da hier teilweise die Sammelrechnungen oder Zahlungen erst in nächsten Monat erfolgen
|
||||
|
||||
|
||||
public function exportFullList($paid = 1){
|
||||
|
||||
|
||||
$date_start = Carbon::parse('01.'.session('payment_sales_vol_filter_month').'.'.session('payment_sales_vol_filter_year'))->format('Y-m-d H:i:s');
|
||||
$date_end = Carbon::parse('01.'.session('payment_sales_vol_filter_month').'.'.session('payment_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d H:i:s');
|
||||
$ShoppingOrders = ShoppingOrder::where('paid', $paid)->where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
|
||||
$txActions = ['prev' => 'keine Zahlung', 'appointed' => 'offen', 'failed' => 'abbruch', 'paid' => 'bezahlt'];
|
||||
$headers = array('ID', 'Zahlung', 'Datum', 'EMail', 'ProduktID', 'ProduktNummer', 'ProduktName', 'Anzahl', 'Notiz', 'Gesamt');
|
||||
$objects = [];
|
||||
$columns = [];
|
||||
$hasSOID = [];
|
||||
foreach($ShoppingOrders as $ShoppingOrder){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
$value = "";
|
||||
if($shopping_order_item->product){
|
||||
if(isset($objects[$shopping_order_item->product->id])){
|
||||
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
|
||||
$objects[$shopping_order_item->product->id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shopping_order_item->product->id] = [
|
||||
'name' => $shopping_order_item->product->name,
|
||||
'number' => $shopping_order_item->product->number,
|
||||
'value' => $shopping_order_item->qty
|
||||
];
|
||||
$value = $shopping_order_item->qty;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
$object = [];
|
||||
if(in_array($ShoppingOrder->id, $hasSOID)){
|
||||
$object['ID'] = '';
|
||||
$object['EMail'] = '';
|
||||
$object['Zahlung'] = '';
|
||||
$object['Datum'] = '';
|
||||
}else{
|
||||
$object['ID'] = $ShoppingOrder->id;
|
||||
$object['EMail'] = $ShoppingOrder->shopping_user ? $ShoppingOrder->shopping_user->billing_email : 'n/a';
|
||||
$object['Zahlung'] = isset($txActions[$ShoppingOrder->txaction]) ? $txActions[$ShoppingOrder->txaction] : $ShoppingOrder->txaction;
|
||||
$object['Datum'] = $ShoppingOrder->created_at->format('d.m.Y');
|
||||
}
|
||||
$object['ProduktID'] = $shopping_order_item->product_id;
|
||||
$object['ProduktNummer'] = $shopping_order_item->product ? $shopping_order_item->product->number : "n/a";
|
||||
$object['ProduktName'] = $shopping_order_item->product ? $shopping_order_item->product->name : "n/a";
|
||||
$object['Anzahl'] = $shopping_order_item->qty;
|
||||
$object['Notiz'] = ($shopping_order_item->comp ? 'Compensation '.$shopping_order_item->comp : '') . ($shopping_order_item->shopping_collect_order_id ? 'Sammelbestellung '.$shopping_order_item->shopping_collect_order_id : '');
|
||||
$object['Gesamt'] = $value;
|
||||
$columns[] = $object;
|
||||
|
||||
$hasSOID[] = $ShoppingOrder->id;
|
||||
}
|
||||
}
|
||||
if($paid){
|
||||
$filename = "mivita-absatzmengen-full-paid-".session('payment_sales_vol_filter_month').'_'.session('payment_sales_vol_filter_year')."-export";
|
||||
}else{
|
||||
$filename = "mivita-absatzmengen-full-unpaid-".session('payment_sales_vol_filter_month').'_'.session('payment_sales_vol_filter_year')."-export";
|
||||
}
|
||||
|
||||
return Excel::download(new xExport($columns, $headers), $filename.'.xls');
|
||||
|
||||
//CSV EXPORT function
|
||||
$headers = array(
|
||||
"Content-type" => "text/csv",
|
||||
"Content-Disposition" => "attachment; filename=$fileName",
|
||||
"Pragma" => "no-cache",
|
||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
||||
"Expires" => "0"
|
||||
);
|
||||
|
||||
$header = array('ID', 'Zahlung', 'Datum', 'EMail', 'ProduktID', 'ProduktNummer', 'ProduktName', 'Anzahl', 'Notiz', 'Gesamt');
|
||||
|
||||
$callback = function() use($columns, $header) {
|
||||
|
||||
$file = fopen('php://output', 'w');
|
||||
fputcsv($file, $header);
|
||||
$row = [];
|
||||
|
||||
foreach ($columns as $row) {
|
||||
fputcsv($file, $row);
|
||||
|
||||
}
|
||||
|
||||
fclose($file);
|
||||
};
|
||||
|
||||
return response()->stream($callback, 200, $headers);
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
//alte Funktion auswerung nach ShoppingOrder
|
||||
private function testCheckFunction(){
|
||||
//$date_start = Carbon::parse('01.'.session('payment_sales_vol_filter_month').'.'.session('payment_sales_vol_filter_year'))->format('Y-m-d');
|
||||
//$date_end = Carbon::parse('01.'.session('payment_sales_vol_filter_month').'.'.session('payment_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d');
|
||||
|
||||
$date_start = Carbon::parse('01.01.2024')->format('Y-m-d H:i:s');
|
||||
$date_end = Carbon::parse('01.01.2024')->endOfMonth()->format('Y-m-d H:i:s');
|
||||
dump($date_start);
|
||||
dump($date_end);
|
||||
|
||||
$ShoppingOrders = ShoppingOrder::where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
|
||||
$objects = [];
|
||||
$counter = 0;
|
||||
foreach($ShoppingOrders as $ShoppingOrder){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
|
||||
if($shopping_order_item->product){
|
||||
if($shopping_order_item->product->id === 122){
|
||||
//dump($shopping_order_item->qty);
|
||||
//$counter += $shopping_order_item->qty;
|
||||
if(isset($objects[$shopping_order_item->product->id])){
|
||||
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
|
||||
$objects[$shopping_order_item->product->id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shopping_order_item->product->id] = [
|
||||
'name' => $shopping_order_item->product->name,
|
||||
'number' => $shopping_order_item->product->number,
|
||||
'value' => $shopping_order_item->qty
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$ShoppingOrderItems = ShoppingOrderItem::whereProductId(122)->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
$counter = 0;
|
||||
foreach($ShoppingOrderItems as $ShoppingOrderItem){
|
||||
$counter += $ShoppingOrderItem->qty;
|
||||
dump($ShoppingOrderItem->id);
|
||||
}
|
||||
// dump($objects);
|
||||
dump($counter);
|
||||
dd("OKAY");
|
||||
}*/
|
||||
|
||||
/*
|
||||
// alte Funktion auswerung nach ShoppingOrder
|
||||
private function initSearch($returnColl = true)
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
$date_start = Carbon::parse('01.'.session('payment_sales_vol_filter_month').'.'.session('payment_sales_vol_filter_year'))->format('Y-m-d H:i:s');
|
||||
$date_end = Carbon::parse('01.'.session('payment_sales_vol_filter_month').'.'.session('payment_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d H:i:s');
|
||||
$ShoppingOrders = ShoppingOrder::where('paid', 1)->where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
|
||||
$objects = [];
|
||||
foreach($ShoppingOrders as $ShoppingOrder){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
if($shopping_order_item->product){
|
||||
if(isset($objects[$shopping_order_item->product->id])){
|
||||
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
|
||||
$objects[$shopping_order_item->product->id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shopping_order_item->product->id] = [
|
||||
'name' => $shopping_order_item->product->name,
|
||||
'number' => $shopping_order_item->product->number,
|
||||
'value' => $shopping_order_item->qty
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($returnColl){
|
||||
$collection = collect();
|
||||
|
||||
foreach($objects as $key => $obj){
|
||||
$collection->push([
|
||||
'id' => $key,
|
||||
'name' => $obj['name'],
|
||||
'number' => $obj['number'],
|
||||
'value' => $obj['value'],
|
||||
]);
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
return $objects;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
@ -1,342 +0,0 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
use Auth;
|
||||
use Request;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use App\Exports\xExport;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Exports\UserTeamExport;
|
||||
use App\Models\ShoppingOrderItem;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use App\Services\BusinessPlan\ExportBot;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ProductsSalesController extends Controller
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('admin');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
$this->setFilterVars();
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_years' => HTMLHelper::getYearRange(2022),
|
||||
];
|
||||
return view('admin.payment.salesvolume', $data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function download(){
|
||||
|
||||
/*
|
||||
EXCEL EXPORT function */
|
||||
|
||||
if(Request::get('action') === "exportfull_paid"){
|
||||
return $this->exportFullList(1);
|
||||
}
|
||||
if(Request::get('action') === "exportfull_unpaid"){
|
||||
return $this->exportFullList(0);
|
||||
}
|
||||
|
||||
if(Request::get('action') === "exportfull_paid_invoice"){
|
||||
return $this->exportFullListInvoice();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(Request::get('action') === "export"){
|
||||
$objects = $this->initSearch(false);
|
||||
$columns = [];
|
||||
$filename = "mivita-absatzmengen-".session('product_sales_vol_filter_month').'_'.session('product_sales_vol_filter_year')."-export";
|
||||
$headers = array(
|
||||
'#',
|
||||
'Produkt',
|
||||
'Artikelnummer',
|
||||
'Menge',
|
||||
|
||||
);
|
||||
if($objects){
|
||||
foreach ($objects as $key => $obj){
|
||||
$columns[] = array(
|
||||
'id' => $key,
|
||||
'name' => $obj['name'],
|
||||
'number' => $obj['number'],
|
||||
'value' => $obj['value'],
|
||||
);
|
||||
}
|
||||
}
|
||||
return Excel::download(new UserTeamExport($columns, $headers), $filename.'.xls');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function setFilterVars(){
|
||||
|
||||
if(!session('product_sales_vol_filter_month')){
|
||||
session(['product_sales_vol_filter_month' => intval(date('m'))]);
|
||||
}
|
||||
if(!session('product_sales_vol_filter_year')){
|
||||
session(['product_sales_vol_filter_year' => intval(date('Y'))]);
|
||||
}
|
||||
|
||||
if(Request::get('product_sales_vol_filter_month')){
|
||||
session(['product_sales_vol_filter_month' => Request::get('product_sales_vol_filter_month')]);
|
||||
}
|
||||
if(Request::get('product_sales_vol_filter_year')){
|
||||
session(['product_sales_vol_filter_year' => Request::get('product_sales_vol_filter_year')]);
|
||||
}
|
||||
}
|
||||
|
||||
public function exportFullList($paid = 1){
|
||||
|
||||
$date_start = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->format('Y-m-d H:i:s');
|
||||
$date_end = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d H:i:s');
|
||||
$ShoppingOrders = ShoppingOrder::where('paid', $paid)->where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
|
||||
$txActions = ['prev' => 'keine Zahlung', 'appointed' => 'offen', 'failed' => 'abbruch', 'paid' => 'bezahlt'];
|
||||
$headers = array('ID', 'Zahlung', 'Datum', 'EMail', 'ProduktID', 'ProduktNummer', 'ProduktName', 'Anzahl', 'Notiz', 'Gesamt');
|
||||
$objects = [];
|
||||
$columns = [];
|
||||
$hasSOID = [];
|
||||
foreach($ShoppingOrders as $ShoppingOrder){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
$value = "";
|
||||
if($shopping_order_item->product){
|
||||
if(isset($objects[$shopping_order_item->product->id])){
|
||||
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
|
||||
$objects[$shopping_order_item->product->id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shopping_order_item->product->id] = [
|
||||
'name' => $shopping_order_item->product->name,
|
||||
'number' => $shopping_order_item->product->number,
|
||||
'value' => $shopping_order_item->qty
|
||||
];
|
||||
$value = $shopping_order_item->qty;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
$object = [];
|
||||
if(in_array($ShoppingOrder->id, $hasSOID)){
|
||||
$object['ID'] = '';
|
||||
$object['EMail'] = '';
|
||||
$object['Zahlung'] = '';
|
||||
$object['Datum'] = '';
|
||||
}else{
|
||||
$object['ID'] = $ShoppingOrder->id;
|
||||
$object['EMail'] = $ShoppingOrder->shopping_user ? $ShoppingOrder->shopping_user->billing_email : 'n/a';
|
||||
$object['Zahlung'] = isset($txActions[$ShoppingOrder->txaction]) ? $txActions[$ShoppingOrder->txaction] : $ShoppingOrder->txaction;
|
||||
$object['Datum'] = $ShoppingOrder->created_at->format('d.m.Y');
|
||||
}
|
||||
$object['ProduktID'] = $shopping_order_item->product_id;
|
||||
$object['ProduktNummer'] = $shopping_order_item->product ? $shopping_order_item->product->number : "n/a";
|
||||
$object['ProduktName'] = $shopping_order_item->product ? $shopping_order_item->product->name : "n/a";
|
||||
$object['Anzahl'] = $shopping_order_item->qty;
|
||||
$object['Notiz'] = ($shopping_order_item->comp ? 'Compensation '.$shopping_order_item->comp : '') . ($shopping_order_item->shopping_collect_order_id ? 'Sammelbestellung '.$shopping_order_item->shopping_collect_order_id : '');
|
||||
$object['Gesamt'] = $value;
|
||||
$columns[] = $object;
|
||||
|
||||
$hasSOID[] = $ShoppingOrder->id;
|
||||
}
|
||||
}
|
||||
if($paid){
|
||||
$filename = "mivita-absatzmengen-full-paid-".session('product_sales_vol_filter_month').'_'.session('product_sales_vol_filter_year')."-export";
|
||||
}else{
|
||||
$filename = "mivita-absatzmengen-full-unpaid-".session('product_sales_vol_filter_month').'_'.session('product_sales_vol_filter_year')."-export";
|
||||
}
|
||||
|
||||
return Excel::download(new xExport($columns, $headers), $filename.'.xls');
|
||||
|
||||
/* CSV EXPORT function
|
||||
$headers = array(
|
||||
"Content-type" => "text/csv",
|
||||
"Content-Disposition" => "attachment; filename=$fileName",
|
||||
"Pragma" => "no-cache",
|
||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
||||
"Expires" => "0"
|
||||
);
|
||||
|
||||
$header = array('ID', 'Zahlung', 'Datum', 'EMail', 'ProduktID', 'ProduktNummer', 'ProduktName', 'Anzahl', 'Notiz', 'Gesamt');
|
||||
|
||||
$callback = function() use($columns, $header) {
|
||||
|
||||
$file = fopen('php://output', 'w');
|
||||
fputcsv($file, $header);
|
||||
$row = [];
|
||||
|
||||
foreach ($columns as $row) {
|
||||
fputcsv($file, $row);
|
||||
|
||||
}
|
||||
|
||||
fclose($file);
|
||||
};
|
||||
|
||||
return response()->stream($callback, 200, $headers);
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
private function initSearch($returnColl = true)
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
$date_start = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->format('Y-m-d H:i:s');
|
||||
$date_end = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d H:i:s');
|
||||
$ShoppingOrders = ShoppingOrder::where('paid', 1)->where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
|
||||
$objects = [];
|
||||
foreach($ShoppingOrders as $ShoppingOrder){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
if($shopping_order_item->product){
|
||||
if(isset($objects[$shopping_order_item->product->id])){
|
||||
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
|
||||
$objects[$shopping_order_item->product->id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shopping_order_item->product->id] = [
|
||||
'name' => $shopping_order_item->product->name,
|
||||
'number' => $shopping_order_item->product->number,
|
||||
'value' => $shopping_order_item->qty
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($returnColl){
|
||||
$collection = collect();
|
||||
|
||||
foreach($objects as $key => $obj){
|
||||
$collection->push([
|
||||
'id' => $key,
|
||||
'name' => $obj['name'],
|
||||
'number' => $obj['number'],
|
||||
'value' => $obj['value'],
|
||||
]);
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
return $objects;
|
||||
}
|
||||
|
||||
|
||||
public function datatable(){
|
||||
|
||||
$collection = $this->initSearch(true);
|
||||
|
||||
$collect = collect([
|
||||
['id' => 1, 'name' => 'John', 'number'=>92012, 'value'=>123],
|
||||
['id' => 2, 'name' => 'Jane', 'number'=>92012, 'value'=>123],
|
||||
['id' => 3, 'name' => 'James', 'number'=>92012, 'value'=>123],
|
||||
]);
|
||||
|
||||
return \DataTables::of($collection)->toJson();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*private function export_vp(){
|
||||
|
||||
$query = User::with('account')->select('users.*')->where('users.deleted_at', '=', null)->where('users.admin', "<", 4)->get();
|
||||
$fileName = "GS-VP-export-".date("d-m-Y").".csv";
|
||||
$headers = array(
|
||||
"Content-type" => "text/csv",
|
||||
"Content-Disposition" => "attachment; filename=$fileName",
|
||||
"Pragma" => "no-cache",
|
||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
||||
"Expires" => "0"
|
||||
);
|
||||
|
||||
$columns = array('ID', 'Email', 'Firma', 'Anrede', 'Vorname', 'Nachname', 'Mitglied', 'Bis');
|
||||
|
||||
$callback = function() use($query, $columns) {
|
||||
|
||||
$file = fopen('php://output', 'w');
|
||||
fputcsv($file, $columns);
|
||||
$row = [];
|
||||
|
||||
foreach ($query as $val) {
|
||||
$row['ID'] = $val->id;
|
||||
$row['Email'] = $val->email;
|
||||
$row['Firma'] = $val->account->company;
|
||||
$row['Anrede'] = $val->account->salutation == 'mr' ? 'Herr' : 'Frau' ;
|
||||
$row['Vorname'] = $val->account->first_name;
|
||||
$row['Nachname'] = $val->account->last_name;
|
||||
$row['Mitglied'] = $val->payment_account ? ($val->isActiveAccount() ? 'JA' : 'Abgelaufen') : "Nein";
|
||||
$row['Bis'] = $val->payment_account ? $val->getPaymentAccountDateFormat(false) : "-";
|
||||
fputcsv($file, $row);
|
||||
|
||||
}
|
||||
|
||||
fclose($file);
|
||||
};
|
||||
|
||||
return response()->stream($callback, 200, $headers);
|
||||
|
||||
//dd("ok");
|
||||
}*/
|
||||
|
||||
/*private function testCheckFunction(){
|
||||
|
||||
//$date_start = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->format('Y-m-d');
|
||||
//$date_end = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d');
|
||||
|
||||
$date_start = Carbon::parse('01.01.2024')->format('Y-m-d H:i:s');
|
||||
$date_end = Carbon::parse('01.01.2024')->endOfMonth()->format('Y-m-d H:i:s');
|
||||
dump($date_start);
|
||||
dump($date_end);
|
||||
|
||||
$ShoppingOrders = ShoppingOrder::where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
|
||||
$objects = [];
|
||||
$counter = 0;
|
||||
foreach($ShoppingOrders as $ShoppingOrder){
|
||||
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
|
||||
|
||||
if($shopping_order_item->product){
|
||||
if($shopping_order_item->product->id === 122){
|
||||
//dump($shopping_order_item->qty);
|
||||
//$counter += $shopping_order_item->qty;
|
||||
if(isset($objects[$shopping_order_item->product->id])){
|
||||
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
|
||||
$objects[$shopping_order_item->product->id]['value'] = $value;
|
||||
}else{
|
||||
$objects[$shopping_order_item->product->id] = [
|
||||
'name' => $shopping_order_item->product->name,
|
||||
'number' => $shopping_order_item->product->number,
|
||||
'value' => $shopping_order_item->qty
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$ShoppingOrderItems = ShoppingOrderItem::whereProductId(122)->whereBetween('created_at', [$date_start, $date_end])->get();
|
||||
$counter = 0;
|
||||
foreach($ShoppingOrderItems as $ShoppingOrderItem){
|
||||
$counter += $ShoppingOrderItem->qty;
|
||||
dump($ShoppingOrderItem->id);
|
||||
}
|
||||
// dump($objects);
|
||||
dump($counter);
|
||||
dd("OKAY");
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,708 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\ShoppingUser;
|
||||
use App\Models\ShoppingUserMemberLog;
|
||||
use App\Models\UserCleanUpLog;
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AdminUserCleanupController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('superadmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Übersicht deaktivierter und gelöschter User
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('admin.user.cleanup.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Protokoll der User-Cleanup-Logs (Downline-Übertragungen)
|
||||
*/
|
||||
public function logs()
|
||||
{
|
||||
return view('admin.user.cleanup.logs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Protokoll der Shopping-User-Member-Logs (Kunden-Übertragungen)
|
||||
*/
|
||||
public function shoppingLogs()
|
||||
{
|
||||
return view('admin.user.cleanup.shopping_logs');
|
||||
}
|
||||
|
||||
/**
|
||||
* DataTable für deaktivierte/gelöschte User
|
||||
*/
|
||||
public function getInactiveUsers()
|
||||
{
|
||||
// Deaktivierte User (active=false) ODER gelöschte User (mit pre_deleted_at)
|
||||
$query = User::withTrashed()
|
||||
->where(function ($q) {
|
||||
$q->where('active', false)
|
||||
->orWhere('pre_deleted_at', '!=', null);
|
||||
})
|
||||
->with('account')
|
||||
->select('users.*')
|
||||
->where('users.admin', '<', 5);
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('user_id', function (User $user) {
|
||||
return $user->id;
|
||||
})
|
||||
->addColumn('first_name', function (User $user) {
|
||||
return $user->account ? $user->account->first_name : '';
|
||||
})
|
||||
->addColumn('last_name', function (User $user) {
|
||||
return $user->account ? $user->account->last_name : '';
|
||||
})
|
||||
->addColumn('email', function (User $user) {
|
||||
if ($user->pre_deleted_at) {
|
||||
return '<span class="badge badge-pill badge-danger">'.$user->email.'</span>';
|
||||
}
|
||||
|
||||
return $user->email;
|
||||
})
|
||||
->addColumn('m_account', function (User $user) {
|
||||
return $user->account ? $user->account->m_account : '';
|
||||
})
|
||||
->addColumn('status', function (User $user) {
|
||||
if ($user->pre_deleted_at) {
|
||||
return '<span class="badge badge-danger">Gelöscht</span>';
|
||||
}
|
||||
if (! $user->active) {
|
||||
return '<span class="badge badge-warning">Deaktiviert</span>';
|
||||
}
|
||||
|
||||
return '<span class="badge badge-success">Aktiv</span>';
|
||||
})
|
||||
->addColumn('deleted_at', function (User $user) {
|
||||
if ($user->pre_deleted_at) {
|
||||
return \Carbon\Carbon::parse($user->pre_deleted_at)->format('d.m.Y H:i');
|
||||
}
|
||||
|
||||
return '-';
|
||||
})
|
||||
->addColumn('payment_account', function (User $user) {
|
||||
return $user->getPaymentAccountDateFormat();
|
||||
})
|
||||
->addColumn('m_sponsor', function (User $user) {
|
||||
if ($user->m_sponsor) {
|
||||
$sponsor = User::find($user->m_sponsor);
|
||||
|
||||
return $sponsor ? $sponsor->email : 'ID: '.$user->m_sponsor;
|
||||
}
|
||||
|
||||
return '-';
|
||||
})
|
||||
->addColumn('pre_sponsor', function (User $user) {
|
||||
if ($user->pre_sponsor) {
|
||||
$sponsor = User::withTrashed()->find($user->pre_sponsor);
|
||||
|
||||
return $sponsor ? $sponsor->email : 'ID: '.$user->pre_sponsor;
|
||||
}
|
||||
|
||||
return '-';
|
||||
})
|
||||
->addColumn('childs_count', function (User $user) {
|
||||
$count = User::where('m_sponsor', $user->id)->count();
|
||||
|
||||
return $count > 0 ? '<span class="badge badge-info">'.$count.'</span>' : '0';
|
||||
})
|
||||
->addColumn('shopping_users_count', function (User $user) {
|
||||
$count = ShoppingUser::where('member_id', $user->id)->count();
|
||||
|
||||
return $count > 0 ? '<span class="badge badge-info">'.$count.'</span>' : '0';
|
||||
})
|
||||
->addColumn('action', function (User $user) {
|
||||
$html = '';
|
||||
if ($user->pre_deleted_at) {
|
||||
$html .= '<a href="'.route('admin_lead_edit', [$user->id]).'" class="btn btn-sm btn-info" title="Details"><i class="fa fa-eye"></i></a> ';
|
||||
} else {
|
||||
$html .= '<a href="'.route('admin_lead_edit', [$user->id]).'" class="btn btn-sm btn-primary" title="Bearbeiten"><i class="fa fa-edit"></i></a> ';
|
||||
}
|
||||
|
||||
// Historie-Button
|
||||
$html .= ' <button class="btn btn-sm btn-secondary btn-user-history" data-id="'.$user->id.'" data-email="'.$user->email.'" title="Historie & Details"><i class="fa fa-history"></i></button>';
|
||||
|
||||
// Restore-Button für gelöschte User
|
||||
if ($user->pre_deleted_at) {
|
||||
$html .= ' <button class="btn btn-sm btn-success" data-toggle="modal" data-target="#modal-restore-user" data-id="'.$user->id.'" data-email="'.str_replace('delete-', '', $user->email).'" title="Wiederherstellen"><i class="fa fa-undo"></i></button>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
})
|
||||
->orderColumn('user_id', 'id $1')
|
||||
->orderColumn('email', 'email $1')
|
||||
->orderColumn('status', 'active $1')
|
||||
->rawColumns(['email', 'status', 'childs_count', 'shopping_users_count', 'action'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* DataTable für UserCleanUpLogs (Downline-Übertragungen)
|
||||
*/
|
||||
public function getCleanupLogs()
|
||||
{
|
||||
$query = UserCleanUpLog::with(['inactive_sponsor.account', 'child_user.account', 'new_sponsor.account'])
|
||||
->select('user_clean_up_logs.*');
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (UserCleanUpLog $log) {
|
||||
return $log->id;
|
||||
})
|
||||
->addColumn('inactive_sponsor', function (UserCleanUpLog $log) {
|
||||
if ($log->inactive_sponsor && $log->inactive_sponsor->account) {
|
||||
$name = trim($log->inactive_sponsor->account->first_name.' '.$log->inactive_sponsor->account->last_name);
|
||||
|
||||
return ($name ?: 'N/A').'<br><small>'.$log->inactive_sponsor->email.'</small>';
|
||||
}
|
||||
|
||||
return 'ID: '.$log->inactive_sponsor_id;
|
||||
})
|
||||
->addColumn('child_user', function (UserCleanUpLog $log) {
|
||||
if ($log->child_user && $log->child_user->account) {
|
||||
$name = trim($log->child_user->account->first_name.' '.$log->child_user->account->last_name);
|
||||
|
||||
return ($name ?: 'N/A').'<br><small>'.$log->child_user->email.'</small>';
|
||||
}
|
||||
|
||||
return 'ID: '.$log->child_user_id;
|
||||
})
|
||||
->addColumn('new_sponsor', function (UserCleanUpLog $log) {
|
||||
$html = '';
|
||||
|
||||
// Original-Sponsor aus dem Log
|
||||
if ($log->new_sponsor && $log->new_sponsor->account) {
|
||||
$name = trim($log->new_sponsor->account->first_name.' '.$log->new_sponsor->account->last_name);
|
||||
$html .= '<span class="text-muted"><small>Damals:</small></span><br>';
|
||||
$html .= ($name ?: 'N/A').'<br><small>'.$log->new_sponsor->email.'</small>';
|
||||
} else {
|
||||
$html .= 'ID: '.$log->new_sponsor_id;
|
||||
}
|
||||
|
||||
// Prüfe aktuellen Sponsor des child_user
|
||||
if ($log->child_user) {
|
||||
$currentSponsorId = $log->child_user->m_sponsor;
|
||||
|
||||
// Wenn aktueller Sponsor ANDERS ist als im Log
|
||||
if ($currentSponsorId && $currentSponsorId != $log->new_sponsor_id) {
|
||||
$currentSponsor = User::with('account')->find($currentSponsorId);
|
||||
|
||||
if ($currentSponsor) {
|
||||
$html .= '<hr class="my-1">';
|
||||
$html .= '<span class="badge badge-warning">Geändert!</span><br>';
|
||||
$html .= '<span class="text-success"><small>Aktuell:</small></span><br>';
|
||||
|
||||
if ($currentSponsor->account) {
|
||||
$currentName = trim($currentSponsor->account->first_name.' '.$currentSponsor->account->last_name);
|
||||
$html .= '<strong>'.($currentName ?: 'N/A').'</strong><br>';
|
||||
}
|
||||
|
||||
$html .= '<small>'.$currentSponsor->email.'</small>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
})
|
||||
->addColumn('created_at', function (UserCleanUpLog $log) {
|
||||
return \Carbon\Carbon::parse($log->created_at)->format('d.m.Y H:i');
|
||||
})
|
||||
->orderColumn('id', 'id $1')
|
||||
->rawColumns(['inactive_sponsor', 'child_user', 'new_sponsor'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* DataTable für ShoppingUserMemberLogs (Kunden-Übertragungen)
|
||||
*/
|
||||
public function getShoppingLogs()
|
||||
{
|
||||
$query = ShoppingUserMemberLog::with(['pre_member.account', 'shopping_user', 'new_member.account'])
|
||||
->select('shopping_user_member_logs.*');
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (ShoppingUserMemberLog $log) {
|
||||
return $log->id;
|
||||
})
|
||||
->addColumn('pre_member', function (ShoppingUserMemberLog $log) {
|
||||
if ($log->pre_member && $log->pre_member->account) {
|
||||
$name = trim($log->pre_member->account->first_name.' '.$log->pre_member->account->last_name);
|
||||
|
||||
return ($name ?: 'N/A').'<br><small>'.$log->pre_member->email.'</small>';
|
||||
}
|
||||
|
||||
return 'ID: '.$log->pre_member_id;
|
||||
})
|
||||
->addColumn('shopping_user', function (ShoppingUserMemberLog $log) {
|
||||
if ($log->shopping_user) {
|
||||
$name = trim($log->shopping_user->billing_firstname.' '.$log->shopping_user->billing_lastname);
|
||||
|
||||
return ($name ?: 'N/A').'<br><small>'.$log->shopping_user->billing_email.'</small>';
|
||||
}
|
||||
|
||||
return 'ID: '.$log->shopping_user_id;
|
||||
})
|
||||
->addColumn('new_member', function (ShoppingUserMemberLog $log) {
|
||||
$html = '';
|
||||
|
||||
// Original-Berater aus dem Log
|
||||
if ($log->new_member && $log->new_member->account) {
|
||||
$name = trim($log->new_member->account->first_name.' '.$log->new_member->account->last_name);
|
||||
$html .= '<span class="text-muted"><small>Damals:</small></span><br>';
|
||||
$html .= ($name ?: 'N/A').'<br><small>'.$log->new_member->email.'</small>';
|
||||
} else {
|
||||
$html .= 'ID: '.$log->new_member_id;
|
||||
}
|
||||
|
||||
// Prüfe aktuellen Berater des Kunden
|
||||
if ($log->shopping_user) {
|
||||
$currentMemberId = $log->shopping_user->member_id;
|
||||
|
||||
// Wenn aktueller Berater ANDERS ist als im Log
|
||||
if ($currentMemberId && $currentMemberId != $log->new_member_id) {
|
||||
$currentMember = User::with('account')->find($currentMemberId);
|
||||
|
||||
if ($currentMember) {
|
||||
$html .= '<hr class="my-1">';
|
||||
$html .= '<span class="badge badge-warning">Geändert!</span><br>';
|
||||
$html .= '<span class="text-success"><small>Aktuell:</small></span><br>';
|
||||
|
||||
if ($currentMember->account) {
|
||||
$currentName = trim($currentMember->account->first_name.' '.$currentMember->account->last_name);
|
||||
$html .= '<strong>'.($currentName ?: 'N/A').'</strong><br>';
|
||||
}
|
||||
|
||||
$html .= '<small>'.$currentMember->email.'</small>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
})
|
||||
->addColumn('created_at', function (ShoppingUserMemberLog $log) {
|
||||
return \Carbon\Carbon::parse($log->created_at)->format('d.m.Y H:i');
|
||||
})
|
||||
->orderColumn('id', 'id $1')
|
||||
->rawColumns(['pre_member', 'shopping_user', 'new_member'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* User über Artisan Command wiederherstellen
|
||||
*/
|
||||
public function restore(\Illuminate\Http\Request $request)
|
||||
{
|
||||
$userId = $request->input('user_id');
|
||||
|
||||
if (! $userId) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Keine User-ID angegeben',
|
||||
], 400);
|
||||
}
|
||||
|
||||
try {
|
||||
// Führe Artisan Command aus
|
||||
\Artisan::call('user:restore', ['user_id' => $userId]);
|
||||
|
||||
$output = \Artisan::output();
|
||||
|
||||
// Prüfe ob Command erfolgreich war (Exit Code 0)
|
||||
$exitCode = \Artisan::call('user:restore', ['user_id' => $userId]);
|
||||
|
||||
\Log::channel('cleanup')->info('AdminUserCleanupController restore via web: user_id='.$userId.' | exitCode='.$exitCode);
|
||||
|
||||
if ($exitCode === 0) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'User wurde erfolgreich wiederhergestellt',
|
||||
'output' => $output,
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Wiederherstellen (Exit Code: '.$exitCode.')',
|
||||
'output' => $output,
|
||||
], 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::channel('cleanup')->error('AdminUserCleanupController restore failed: '.$e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Exception: '.$e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suche nach aktiven Sponsoren für Select2
|
||||
*/
|
||||
public function searchSponsors(\Illuminate\Http\Request $request)
|
||||
{
|
||||
$search = $request->input('q');
|
||||
$userId = $request->input('exclude_user_id'); // User selbst ausschließen
|
||||
$loadAll = $request->input('load_all', false); // Alle Sponsoren laden
|
||||
|
||||
$query = User::where('active', true)
|
||||
->where('admin', '<', 5)
|
||||
->where('blocked', false)
|
||||
->where('payment_account', '>=', now())
|
||||
->with('account')
|
||||
->orderBy('email', 'asc');
|
||||
|
||||
if ($userId) {
|
||||
$query->where('id', '!=', $userId);
|
||||
}
|
||||
|
||||
// Nur filtern wenn Suche vorhanden und nicht load_all
|
||||
if ($search && ! $loadAll) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('email', 'like', '%'.$search.'%')
|
||||
->orWhereHas('account', function ($q2) use ($search) {
|
||||
$q2->where('first_name', 'like', '%'.$search.'%')
|
||||
->orWhere('last_name', 'like', '%'.$search.'%')
|
||||
->orWhere('m_account', 'like', '%'.$search.'%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Limit nur wenn nicht alle geladen werden sollen
|
||||
if (! $loadAll) {
|
||||
$query->limit(20);
|
||||
}
|
||||
|
||||
$users = $query->get()->map(function ($user) {
|
||||
$name = '';
|
||||
if ($user->account) {
|
||||
$name = trim($user->account->first_name.' '.$user->account->last_name);
|
||||
if ($name) {
|
||||
$name .= ' | ';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'text' => $name.$user->email.($user->account && $user->account->m_account ? ' #'.$user->account->m_account : ''),
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json(['results' => $users]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sponsor manuell neu zuweisen
|
||||
*/
|
||||
public function reassignSponsor(\Illuminate\Http\Request $request)
|
||||
{
|
||||
$userId = $request->input('user_id');
|
||||
$newSponsorId = $request->input('new_sponsor_id');
|
||||
// Boolean-Werte korrekt konvertieren (auch wenn sie als String ankommen)
|
||||
$transferDownline = filter_var($request->input('transfer_downline', false), FILTER_VALIDATE_BOOLEAN);
|
||||
$transferCustomers = filter_var($request->input('transfer_customers', false), FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
if (! $userId || ! $newSponsorId) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'User-ID und neuer Sponsor sind erforderlich',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$user = User::withTrashed()->find($userId);
|
||||
$newSponsor = User::find($newSponsorId);
|
||||
|
||||
if (! $user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'User nicht gefunden',
|
||||
], 404);
|
||||
}
|
||||
|
||||
if (! $newSponsor || ! $newSponsor->active) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Neuer Sponsor nicht gefunden oder nicht aktiv',
|
||||
], 404);
|
||||
}
|
||||
|
||||
\DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$oldSponsorId = $user->m_sponsor;
|
||||
$logs = [];
|
||||
|
||||
// 1. Downline neu zuweisen (aus Logs - bereits übertragene)
|
||||
$childrenTransferred = 0;
|
||||
if ($transferDownline) {
|
||||
// Hole die Kinder aus den vorherigen Cleanup-Logs (die bereits VON diesem User weg übertragen wurden)
|
||||
$cleanupLogs = UserCleanUpLog::where('inactive_sponsor_id', $userId)->get();
|
||||
|
||||
\Log::channel('cleanup')->info('Reassigning downline from logs: found '.$cleanupLogs->count().' log entries for user_id='.$userId);
|
||||
|
||||
foreach ($cleanupLogs as $oldLog) {
|
||||
$child = User::find($oldLog->child_user_id);
|
||||
|
||||
if (! $child) {
|
||||
\Log::channel('cleanup')->warning('Child user not found: '.$oldLog->child_user_id);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Neuen Log erstellen für die Neu-Zuweisung
|
||||
UserCleanUpLog::create([
|
||||
'inactive_sponsor_id' => $child->m_sponsor, // Aktueller Sponsor (wohin es vorher übertragen wurde)
|
||||
'child_user_id' => $child->id,
|
||||
'new_sponsor_id' => $newSponsorId, // Neuer Sponsor
|
||||
]);
|
||||
|
||||
// Sponsor ändern
|
||||
$child->m_sponsor = $newSponsorId;
|
||||
$child->save();
|
||||
|
||||
$childrenTransferred++;
|
||||
|
||||
$logs[] = 'Downline: '.$child->email.' → Neuer Sponsor: '.$newSponsor->email;
|
||||
}
|
||||
|
||||
\Log::channel('cleanup')->info('Children reassigned: '.$childrenTransferred);
|
||||
}
|
||||
|
||||
// 2. Shopping-Kunden neu zuweisen (aus Logs - bereits übertragene)
|
||||
$customersTransferred = 0;
|
||||
if ($transferCustomers) {
|
||||
// Hole die Kunden aus den vorherigen Shopping-Logs (die bereits VON diesem User weg übertragen wurden)
|
||||
$shoppingLogs = ShoppingUserMemberLog::where('pre_member_id', $userId)->get();
|
||||
|
||||
\Log::channel('cleanup')->info('Reassigning customers from logs: found '.$shoppingLogs->count().' log entries for user_id='.$userId);
|
||||
|
||||
foreach ($shoppingLogs as $oldLog) {
|
||||
$customer = ShoppingUser::find($oldLog->shopping_user_id);
|
||||
|
||||
if (! $customer) {
|
||||
\Log::channel('cleanup')->warning('Shopping user not found: '.$oldLog->shopping_user_id);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Neuen Log erstellen für die Neu-Zuweisung
|
||||
ShoppingUserMemberLog::create([
|
||||
'pre_member_id' => $customer->member_id, // Aktueller Berater (wohin es vorher übertragen wurde)
|
||||
'shopping_user_id' => $customer->id,
|
||||
'new_member_id' => $newSponsorId, // Neuer Berater
|
||||
]);
|
||||
|
||||
// Member ändern
|
||||
$customer->member_id = $newSponsorId;
|
||||
$customer->save();
|
||||
|
||||
$customersTransferred++;
|
||||
|
||||
$logs[] = 'Kunde: '.$customer->billing_email.' → Neuer Berater: '.$newSponsor->email;
|
||||
}
|
||||
|
||||
\Log::channel('cleanup')->info('Customers reassigned: '.$customersTransferred);
|
||||
}
|
||||
|
||||
// 3. User selbst dem neuen Sponsor zuweisen
|
||||
$user->m_sponsor = $newSponsorId;
|
||||
$user->save();
|
||||
|
||||
\DB::commit();
|
||||
|
||||
// Cleanup-Log
|
||||
\Log::channel('cleanup')->info('Manual reassign sponsor: user_id='.$userId.' | old_sponsor='.$oldSponsorId.' | new_sponsor='.$newSponsorId.' | transfer_downline='.(int) $transferDownline.' | transfer_customers='.(int) $transferCustomers.' | by_admin='.Auth::id());
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Sponsor erfolgreich neu zugewiesen',
|
||||
'logs' => $logs,
|
||||
'transferred' => [
|
||||
'downline' => $childrenTransferred,
|
||||
'customers' => $customersTransferred,
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollBack();
|
||||
\Log::channel('cleanup')->error('Manual reassign sponsor failed: user_id='.$userId.' | error='.$e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Neu-Zuweisen: '.$e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lade User-Historie: Downline-Position und Shopping-Kunden
|
||||
*/
|
||||
public function getUserHistory($userId)
|
||||
{
|
||||
$user = User::withTrashed()->with('account')->find($userId);
|
||||
|
||||
if (! $user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'User nicht gefunden',
|
||||
], 404);
|
||||
}
|
||||
|
||||
// Aktueller/Vorheriger Sponsor
|
||||
$sponsor = null;
|
||||
$preSponsor = null;
|
||||
|
||||
if ($user->m_sponsor) {
|
||||
$sponsor = User::withTrashed()->with('account')->find($user->m_sponsor);
|
||||
}
|
||||
|
||||
if ($user->pre_sponsor) {
|
||||
$preSponsor = User::withTrashed()->with('account')->find($user->pre_sponsor);
|
||||
}
|
||||
|
||||
// Direkte Downline (Kinder) - nur die, die aktuell m_sponsor haben
|
||||
// pre_sponsor sind bereits deaktiviert und würden doppelt gezählt
|
||||
$children = User::withTrashed()
|
||||
->with('account')
|
||||
->where('m_sponsor', $userId)
|
||||
->get()
|
||||
->map(function ($child) use ($userId) {
|
||||
return [
|
||||
'id' => $child->id,
|
||||
'name' => $child->account ? trim($child->account->first_name.' '.$child->account->last_name) : 'N/A',
|
||||
'email' => $child->email,
|
||||
'active' => $child->active,
|
||||
'deleted' => $child->pre_deleted_at ? true : false,
|
||||
'is_pre_sponsor' => $child->pre_sponsor == $userId,
|
||||
];
|
||||
});
|
||||
|
||||
// Shopping-Kunden
|
||||
$shoppingUsers = ShoppingUser::where('member_id', $userId)
|
||||
->get()
|
||||
->map(function ($customer) {
|
||||
return [
|
||||
'id' => $customer->id,
|
||||
'name' => trim($customer->billing_firstname.' '.$customer->billing_lastname),
|
||||
'email' => $customer->billing_email,
|
||||
'city' => $customer->billing_city,
|
||||
'created_at' => \Carbon\Carbon::parse($customer->created_at)->format('d.m.Y'),
|
||||
];
|
||||
});
|
||||
|
||||
// Downline-Übertragungen (wo dieser User betroffen war)
|
||||
$cleanupLogs = UserCleanUpLog::with(['inactive_sponsor.account', 'child_user.account', 'new_sponsor.account'])
|
||||
->where(function ($query) use ($userId) {
|
||||
$query->where('inactive_sponsor_id', $userId)
|
||||
->orWhere('child_user_id', $userId);
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->get()
|
||||
->map(function ($log) use ($userId) {
|
||||
$data = [
|
||||
'type' => $log->inactive_sponsor_id == $userId ? 'as_inactive' : 'as_child',
|
||||
'child_user' => $log->child_user ? [
|
||||
'id' => $log->child_user->id,
|
||||
'name' => $log->child_user->account ? trim($log->child_user->account->first_name.' '.$log->child_user->account->last_name) : 'N/A',
|
||||
'email' => $log->child_user->email,
|
||||
'active' => $log->child_user->active,
|
||||
'deleted' => $log->child_user->pre_deleted_at ? true : false,
|
||||
] : null,
|
||||
'inactive_sponsor' => $log->inactive_sponsor ? [
|
||||
'id' => $log->inactive_sponsor->id,
|
||||
'name' => $log->inactive_sponsor->account ? trim($log->inactive_sponsor->account->first_name.' '.$log->inactive_sponsor->account->last_name) : 'N/A',
|
||||
'email' => $log->inactive_sponsor->email,
|
||||
] : null,
|
||||
'new_sponsor' => $log->new_sponsor ? [
|
||||
'id' => $log->new_sponsor->id,
|
||||
'name' => $log->new_sponsor->account ? trim($log->new_sponsor->account->first_name.' '.$log->new_sponsor->account->last_name) : 'N/A',
|
||||
'email' => $log->new_sponsor->email,
|
||||
] : null,
|
||||
'created_at' => \Carbon\Carbon::parse($log->created_at)->format('d.m.Y H:i'),
|
||||
];
|
||||
|
||||
// Prüfe aktuellen Sponsor des child_user (falls geändert)
|
||||
if ($log->child_user && $log->child_user->m_sponsor && $log->child_user->m_sponsor != $log->new_sponsor_id) {
|
||||
$currentSponsor = User::with('account')->find($log->child_user->m_sponsor);
|
||||
if ($currentSponsor) {
|
||||
$data['current_sponsor'] = [
|
||||
'id' => $currentSponsor->id,
|
||||
'name' => $currentSponsor->account ? trim($currentSponsor->account->first_name.' '.$currentSponsor->account->last_name) : 'N/A',
|
||||
'email' => $currentSponsor->email,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
});
|
||||
|
||||
// Kunden-Übertragungen
|
||||
$shoppingLogs = ShoppingUserMemberLog::with(['shopping_user', 'new_member.account'])
|
||||
->where('pre_member_id', $userId)
|
||||
->orderBy('created_at', 'desc')
|
||||
->get()
|
||||
->map(function ($log) {
|
||||
$data = [
|
||||
'customer_name' => $log->shopping_user ? trim($log->shopping_user->billing_firstname.' '.$log->shopping_user->billing_lastname) : 'N/A',
|
||||
'customer_email' => $log->shopping_user ? $log->shopping_user->billing_email : 'N/A',
|
||||
'new_member' => $log->new_member ? [
|
||||
'id' => $log->new_member->id,
|
||||
'name' => $log->new_member->account ? trim($log->new_member->account->first_name.' '.$log->new_member->account->last_name) : 'N/A',
|
||||
'email' => $log->new_member->email,
|
||||
] : null,
|
||||
'created_at' => \Carbon\Carbon::parse($log->created_at)->format('d.m.Y H:i'),
|
||||
];
|
||||
|
||||
// Prüfe aktuellen Berater des Kunden (falls geändert)
|
||||
if ($log->shopping_user && $log->shopping_user->member_id && $log->shopping_user->member_id != $log->new_member_id) {
|
||||
$currentMember = User::with('account')->find($log->shopping_user->member_id);
|
||||
if ($currentMember) {
|
||||
$data['current_member'] = [
|
||||
'id' => $currentMember->id,
|
||||
'name' => $currentMember->account ? trim($currentMember->account->first_name.' '.$currentMember->account->last_name) : 'N/A',
|
||||
'email' => $currentMember->email,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'user' => [
|
||||
'id' => $user->id,
|
||||
'name' => $user->account ? trim($user->account->first_name.' '.$user->account->last_name) : 'N/A',
|
||||
'email' => $user->email,
|
||||
'm_account' => $user->account ? $user->account->m_account : 'N/A',
|
||||
'active' => $user->active,
|
||||
'deleted' => $user->pre_deleted_at ? true : false,
|
||||
'deleted_at' => $user->pre_deleted_at ? \Carbon\Carbon::parse($user->pre_deleted_at)->format('d.m.Y H:i') : null,
|
||||
],
|
||||
'sponsor' => $sponsor ? [
|
||||
'id' => $sponsor->id,
|
||||
'name' => $sponsor->account ? trim($sponsor->account->first_name.' '.$sponsor->account->last_name) : 'N/A',
|
||||
'email' => $sponsor->email,
|
||||
'active' => $sponsor->active,
|
||||
] : null,
|
||||
'pre_sponsor' => $preSponsor ? [
|
||||
'id' => $preSponsor->id,
|
||||
'name' => $preSponsor->account ? trim($preSponsor->account->first_name.' '.$preSponsor->account->last_name) : 'N/A',
|
||||
'email' => $preSponsor->email,
|
||||
'active' => $preSponsor->active,
|
||||
] : null,
|
||||
'children' => $children,
|
||||
'shopping_users' => $shoppingUsers,
|
||||
'cleanup_logs' => $cleanupLogs,
|
||||
'shopping_logs' => $shoppingLogs,
|
||||
]);
|
||||
}
|
||||
}
|
||||
272
app/Http/Controllers/AdminUserController.php
Normal file → Executable file
272
app/Http/Controllers/AdminUserController.php
Normal file → Executable file
|
|
@ -4,19 +4,18 @@ namespace App\Http\Controllers;
|
|||
|
||||
|
||||
|
||||
use Auth;
|
||||
use Carbon;
|
||||
use Request;
|
||||
use App\User;
|
||||
use Validator;
|
||||
use App\Services\SysLog;
|
||||
use App\Services\UserUtil;
|
||||
use App\Models\UserAccount;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\Models\PaymentMethod;
|
||||
use App\Models\UserAccount;
|
||||
use App\Repositories\UserRepository;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\Services\SysLog;
|
||||
use App\User;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Util;
|
||||
use Request;
|
||||
use Validator;
|
||||
|
||||
|
||||
|
||||
class AdminUserController extends Controller
|
||||
{
|
||||
|
|
@ -26,6 +25,7 @@ class AdminUserController extends Controller
|
|||
{
|
||||
$this->middleware('superadmin');
|
||||
$this->userRepo = $userRepo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +43,7 @@ class AdminUserController extends Controller
|
|||
public function edit($user_id)
|
||||
{
|
||||
$user = User::findOrFail($user_id);
|
||||
if (!$user->account) {
|
||||
if(!$user->account){
|
||||
$user->account = new UserAccount();
|
||||
}
|
||||
|
||||
|
|
@ -51,161 +51,119 @@ class AdminUserController extends Controller
|
|||
'user' => $user,
|
||||
];
|
||||
return view('admin.user.edit', $data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||
*/
|
||||
public function store()
|
||||
public function store(Request $request)
|
||||
{
|
||||
|
||||
$data = Request::all();
|
||||
$user = User::withTrashed()->findOrFail($data['id']);
|
||||
|
||||
/* if(isset($data['user-delete'])){
|
||||
$user = User::findOrFail($data['id']);
|
||||
|
||||
if(isset($data['user-delete'])){
|
||||
if(isset($data['realy_delete_user'])){
|
||||
return redirect(route('admin_user_delete', [$user->id]));
|
||||
}
|
||||
}*/
|
||||
if (isset($data['save-admin'])) {
|
||||
}
|
||||
if(isset($data['save-admin'])){
|
||||
$user->admin = $data['admin'];
|
||||
SysLog::action('save-admin', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user admin value: ' . HTMLHelper::getLabel($user->admin))
|
||||
->setMessage('Set user admin value: '.HTMLHelper::getRoleLabel($user->admin))
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($data['save-confirmed'])) {
|
||||
if(isset($data['save-confirmed'])){
|
||||
$data['confirmed'] = isset($data['confirmed']) ? true : false;
|
||||
$user->confirmed = $data['confirmed'];
|
||||
if ($data['confirmed']) {
|
||||
if (!isset($data['confirmation_date']) || $data['confirmation_date'] == "") {
|
||||
if($data['confirmed']){
|
||||
if(!isset($data['confirmation_date']) || $data['confirmation_date'] == ""){
|
||||
$user->confirmation_date = now();
|
||||
} else {
|
||||
}else{
|
||||
$user->confirmation_date = \Carbon::parse(str_replace("- ", "", $data['confirmation_date']));
|
||||
}
|
||||
} else {
|
||||
}else{
|
||||
$user->confirmation_date = null;
|
||||
}
|
||||
SysLog::action('save-confirmed', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user confirmed value: ' . $user->confirmed . " to date: " . $data['confirmation_date'])
|
||||
->setMessage('Set user confirmed value: '.$user->confirmed." to date: ".$data['confirmation_date'])
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($data['save-active'])) {
|
||||
if(isset($data['save-active'])){
|
||||
$data['active'] = isset($data['active']) ? true : false;
|
||||
$user->active = $data['active'];
|
||||
if ($data['active'] === true && $user->wizard < 20) {
|
||||
if($data['active'] === true && $user->wizard < 20){
|
||||
$user->wizard = 20;
|
||||
}
|
||||
if ($data['active']) {
|
||||
if (!isset($data['active_date']) || $data['active_date'] == "") {
|
||||
if($data['active']){
|
||||
if(!isset($data['active_date']) || $data['active_date'] == ""){
|
||||
$user->active_date = now();
|
||||
} else {
|
||||
}else{
|
||||
$user->active_date = \Carbon::parse(str_replace("- ", "", $data['active_date']));
|
||||
}
|
||||
} else {
|
||||
}else{
|
||||
$user->active_date = null;
|
||||
}
|
||||
SysLog::action('save-active', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user active value: ' . $user->active . " to date: " . $data['active_date'])
|
||||
->setMessage('Set user active value: '.$user->active." to date: ".$data['active_date'])
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($data['save-restore'])) {
|
||||
$restore_account = isset($data['restore_account']) ? true : false;
|
||||
$restore_childs = isset($data['restore_childs']) ? true : false;
|
||||
$restore_send_email = isset($data['restore_send_email']) ? true : false;
|
||||
if (isset($data['payment_account']) || $data['payment_account'] != "") {
|
||||
|
||||
$error = UserUtil::checkEmailExists($user);
|
||||
if ($error) {
|
||||
\Session()->flash('alert-error', $error);
|
||||
return redirect()->back()->withInput()->withErrors(['error' => $error]);
|
||||
}
|
||||
$payment_account = \Carbon::parse(str_replace("- ", "", $data['payment_account']));
|
||||
if ($restore_account) {
|
||||
UserUtil::restoreUser($user, $payment_account);
|
||||
if ($restore_childs) {
|
||||
UserUtil::resetChildsToSponsor($user->id);
|
||||
}
|
||||
if ($restore_send_email) {
|
||||
$user = User::with('account')->findOrFail($data['id']);
|
||||
Mail::to($user->email)->send(new \App\Mail\UserRestoreEmail($user));
|
||||
}
|
||||
}
|
||||
}
|
||||
SysLog::action('save-restore', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user restore_account value: ' . $user->restore_account . " and restore_childs value: " . $user->restore_childs)
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($data['save-account'])) {
|
||||
if(isset($data['save-account'])){
|
||||
$old = $user->getPaymentAccountDateFormat(true);
|
||||
if (!isset($data['payment_account']) || $data['payment_account'] == "") {
|
||||
if(!isset($data['payment_account']) || $data['payment_account'] == ""){
|
||||
$user->payment_account = null;
|
||||
} else {
|
||||
}else{
|
||||
$user->wizard = 100;
|
||||
$payment_account = \Carbon::parse(str_replace("- ", "", $data['payment_account']));
|
||||
$user->payment_account = $payment_account;
|
||||
if ($payment_account > Carbon::now()) {
|
||||
if ($user->active === 0) {
|
||||
$user->active = true;
|
||||
UserUtil::reactiveUserResetChilds($user->id, 'on save-account AdminUserController');
|
||||
}
|
||||
} else {
|
||||
if ($user->active === 1) {
|
||||
$user->active = false;
|
||||
UserUtil::deactiveUserNewSponsorChilds($user->id, 'on save-account AdminUserController');
|
||||
}
|
||||
}
|
||||
$user->payment_account = \Carbon::parse(str_replace("- ", "", $data['payment_account']));
|
||||
}
|
||||
//th.schifferegger@gmail.com
|
||||
SysLog::action('save-account', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user payment_account from date: ' . $old . " to date: " . $data['payment_account'])
|
||||
->setMessage('Set user payment_account from date: '.$old." to date: ".$data['payment_account'])
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($data['save-shop'])) {
|
||||
if(isset($data['save-shop'])){
|
||||
$old = $user->getPaymentShopDateFormat(true);
|
||||
if (!isset($data['payment_shop']) || $data['payment_shop'] == "") {
|
||||
if(!isset($data['payment_shop']) || $data['payment_shop'] == ""){
|
||||
$user->payment_shop = null;
|
||||
} else {
|
||||
}else{
|
||||
$user->wizard = 100;
|
||||
$user->payment_shop = \Carbon::parse(str_replace("- ", "", $data['payment_shop']));
|
||||
}
|
||||
SysLog::action('save-shop', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user payment_shop from date: ' . $old . " to date: " . $data['payment_shop'])
|
||||
->setMessage('Set user payment_shop from date: '.$old." to date: ".$data['payment_shop'])
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($data['save-test_mode'])) {
|
||||
if(isset($data['save-test_mode'])){
|
||||
$user->test_mode = isset($data['test_mode']) ? true : false;
|
||||
SysLog::action('save-test_mode', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user test_mode value: ' . $user->test_mode)
|
||||
->setMessage('Set user test_mode value: '.$user->test_mode)
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($data['save-payment_methods'])) {
|
||||
if(isset($data['save-payment_methods'])){
|
||||
$user->payment_methods = isset($data['payment_methods']) ? array_map('intval', $data['payment_methods']) : null;
|
||||
SysLog::action('save-payment_methods', 'admin_user', 3)
|
||||
->setUserId(Auth::user()->id)
|
||||
->setModel($user->id, User::class)
|
||||
->setMessage('Set user payment_methods value: ' . $user->getPaymentMethodsShort())
|
||||
->setMessage('Set user payment_methods value: '.$user->getPaymentMethodsShort())
|
||||
->save();
|
||||
}
|
||||
|
||||
|
|
@ -215,143 +173,23 @@ class AdminUserController extends Controller
|
|||
return redirect('/admin/users');
|
||||
}
|
||||
|
||||
public function deleteUser()
|
||||
public function deleteUser($user_id)
|
||||
{
|
||||
$data = Request::all();
|
||||
$user = User::withTrashed()->findOrFail($data['id']);
|
||||
if (isset($data['realy_delete_user'])) {
|
||||
$this->userRepo->deleteUser($user);
|
||||
\Session()->flash('alert-success', __('msg.contact_delete'));
|
||||
}
|
||||
if (isset($data['realy_delete_user_complete'])) {
|
||||
// $this->userRepo->deleteUserComplete($user);
|
||||
$this->userRepo->deleteUser($user, true);
|
||||
\Session()->flash('alert-success', __('msg.contact_delete'));
|
||||
}
|
||||
$user = User::findOrFail($user_id);
|
||||
$this->userRepo->deleteUser($user);
|
||||
|
||||
\Session()->flash('alert-success', "Kontakt gelöscht");
|
||||
return redirect('/admin/users');
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function userLoginAs($userId)
|
||||
{
|
||||
if (Auth::user()->isSuperAdmin()) {
|
||||
public function userLoginAs($userId){
|
||||
if(Auth::user()->isSuperAdmin()){
|
||||
$user = User::find($userId);
|
||||
Auth::login($user);
|
||||
return redirect('/home');
|
||||
}
|
||||
}
|
||||
|
||||
public function getUsers()
|
||||
{
|
||||
$query = User::withTrashed()
|
||||
->where(function ($q) {
|
||||
$q->where('pre_deleted_at', '!=', null)
|
||||
->orWhere(function ($query) {
|
||||
$query->whereNull('deleted_at')
|
||||
->whereNull('pre_deleted_at');
|
||||
});
|
||||
})
|
||||
->with('account')
|
||||
->select('users.*')
|
||||
->where('users.admin', "<", 5);
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('first_name', function (User $user) {
|
||||
return $user->account ? $user->account->first_name : '';
|
||||
})
|
||||
->addColumn('email', function (User $user) {
|
||||
if ($user->pre_deleted_at) {
|
||||
return '<span class="badge badge-pill badge-danger">' . $user->email . '</span>';
|
||||
}
|
||||
return $user->email;
|
||||
})
|
||||
->addColumn('last_name', function (User $user) {
|
||||
return $user->account ? $user->account->last_name : '';
|
||||
})
|
||||
->addColumn('id', function (User $user) {
|
||||
return '<a href="' . route('admin_lead_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-toggle="modal" data-target="#modals-admin" data-id="' . $user->id . '" data-email="' . $user->email . '" data-admin="' . $user->admin . '">' . HTMLHelper::getRoleLabel($user->admin) . '</a>';
|
||||
})
|
||||
->addColumn('confirmed', function (User $user) {
|
||||
$date = $user->getConfirmationDateFormat();
|
||||
$link = '<a href="#" data-toggle="modal" data-target="#modals-confirmed" data-id="' . $user->id . '" data-email="' . $user->email . '" data-confirmed="' . $user->confirmed . '" data-confirmation_date="' . $date . '">';
|
||||
return $user->confirmed ? $link . '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $date . '</span></a>' : $link . '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span></a>';
|
||||
})
|
||||
->addColumn('active', function (User $user) {
|
||||
if ($user->trashed()) {
|
||||
$date = Carbon::parse(now())->addDays(10)->format('d.m.Y H:i');
|
||||
$email = str_replace("delete-", "", $user->email);
|
||||
$link = '<a href="#" data-toggle="modal" data-target="#modals-restore" data-id="' . $user->id . '" data-email="' . $email . '" data-active="' . $user->active . '" data-payment_account="' . $date . '">';
|
||||
return $link . '<span class="badge badge-pill badge-info"><i class="fa fa-undo"></i> Account reaktivieren</span></a>';
|
||||
}
|
||||
$date = $user->getActiveDateFormat();
|
||||
$link = '<a href="#" data-toggle="modal" data-target="#modals-active" data-id="' . $user->id . '" data-email="' . $user->email . '" data-active="' . $user->active . '" data-active_date="' . $date . '">';
|
||||
return $user->active ? $link . '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $date . '</span></a>' : $link . '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span></a>';
|
||||
})
|
||||
->addColumn('account', function (User $user) {
|
||||
$date = $user->getPaymentAccountDateFormat();
|
||||
$link = '<a href="#" data-toggle="modal" data-target="#modals-account" data-id="' . $user->id . '" data-email="' . $user->email . '" data-payment_account="' . $date . '">';
|
||||
if ($user->payment_account) {
|
||||
if ($user->isActiveAccount()) {
|
||||
return $link . '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $date . '</span></a>';
|
||||
}
|
||||
return $link . '<span class="badge badge-pill badge-warning"><i class="fa fa-ban"></i> ' . $date . '</span></a>';
|
||||
}
|
||||
return $link . '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span></a>';
|
||||
})
|
||||
->addColumn('shop', function (User $user) {
|
||||
$date = $user->getPaymentShopDateFormat();
|
||||
$link = '<a href="#" data-toggle="modal" data-target="#modals-shop" data-id="' . $user->id . '" data-email="' . $user->email . '" data-payment_shop="' . $date . '">';
|
||||
if ($user->payment_shop) {
|
||||
if ($user->isActiveShop()) {
|
||||
return $link . '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $date . '</span></a>';
|
||||
}
|
||||
return $link . '<span class="badge badge-pill badge-warning"><i class="fa fa-ban"></i> ' . $date . '</span></a>';
|
||||
}
|
||||
return $link . '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span></a>';
|
||||
})
|
||||
->addColumn('shop_domain', function (User $user) {
|
||||
return $user->shop ? '<a href="' . $user->shop->getSubdomain(false) . '" target="_blank">' . $user->shop->getSubdomain(false) . '</a>' : '';
|
||||
})
|
||||
->addColumn('since', function (User $user) {
|
||||
if ($user->shop) {
|
||||
if ($user->shop->active) {
|
||||
return $user->shop->getActiveDateFormatSmall();
|
||||
}
|
||||
return $user->shop->getActiveDateFormatSmall();
|
||||
}
|
||||
return "-";
|
||||
})
|
||||
->addColumn('country', function (User $user) {
|
||||
return ($user->account && $user->account->country) ? $user->account->country->de : '';
|
||||
})
|
||||
->addColumn('my_payment_methods', function (User $user) {
|
||||
$payment_methods = json_encode($user->payment_methods);
|
||||
$link = '<a href="#" data-toggle="modal" data-target="#modals-payment_methods" data-id="' . $user->id . '" data-email="' . $user->email . '" data-payment_methods="' . htmlspecialchars($payment_methods) . '">';
|
||||
if (!$user->payment_methods) {
|
||||
return $link . '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span></a>';
|
||||
}
|
||||
return $link . '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i> ' . $user->getPaymentMethodsShort() . '</span></a>';
|
||||
})
|
||||
->addColumn('action_login', function (User $user) {
|
||||
return '<a href="' . route('admin_user_login_as', [$user->id]) . '" class="btn icon-btn btn-sm btn-warning" onclick="return confirm(\'' . __('Login as User?') . '\');"><span class="fa fa-sign-in-alt"></span></a>';
|
||||
})
|
||||
->addColumn('action_delete', function (User $user) {
|
||||
return '<a class="btn icon-btn btn-sm btn-danger" href="#" data-toggle="modal" data-target="#modals-user-delete" data-id="' . $user->id . '" data-email="' . $user->email . '"><span class="fa fa-trash"></span></a>';
|
||||
})
|
||||
->addColumn('test_mode', function (User $user) {
|
||||
$link = '<a href="#" data-toggle="modal" data-target="#modals-test_mode" data-id="' . $user->id . '" data-email="' . $user->email . '" data-test_mode="' . $user->test_mode . '">';
|
||||
return $user->test_mode ? $link . '<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span></a>' : $link . '<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span></a>';
|
||||
})
|
||||
->orderColumn('id', 'id $1')
|
||||
->orderColumn('email', 'email $1')
|
||||
->orderColumn('confirmed', 'confirmed $1')
|
||||
->orderColumn('active', 'active $1')
|
||||
->orderColumn('shop', 'shop $1')
|
||||
->orderColumn('admin', 'active $1')
|
||||
->rawColumns(['id', 'email', 'admin', 'confirmed', 'active', 'account', 'shop', 'shop_domain', 'my_payment_methods', 'test_mode', 'action_login', 'action_delete'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
0
app/Http/Controllers/Api/AuthController.php
Normal file → Executable file
0
app/Http/Controllers/Api/AuthController.php
Normal file → Executable file
|
|
@ -2,60 +2,96 @@
|
|||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Product as ModelsProduct;
|
||||
use Illuminate\Http\Response;
|
||||
use Wearepixel\LaravelGoogleShoppingFeed\LaravelGoogleShoppingFeed;
|
||||
use Vitalybaev\GoogleMerchant\Feed;
|
||||
use Vitalybaev\GoogleMerchant\Product;
|
||||
use Vitalybaev\GoogleMerchant\Product\Shipping;
|
||||
use Vitalybaev\GoogleMerchant\Product\Availability\Availability;
|
||||
use App\Services\Util;
|
||||
|
||||
|
||||
class GoogleMerchantController extends Controller
|
||||
{
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* Generate Google Merchant feed
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function feed()
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$products = ModelsProduct::where('active', true)->whereJsonContains('show_on', '1')->orderBy('pos', 'DESC')->get();
|
||||
|
||||
// Create feed object
|
||||
$feed = LaravelGoogleShoppingFeed::init(
|
||||
'mivita shop',
|
||||
'Bio Aloe Vera & Naturkosmetik',
|
||||
'https://mivita.shop'
|
||||
);
|
||||
|
||||
// Put products to the feed
|
||||
foreach ($products as $product) {
|
||||
$feed->addItem([
|
||||
'id' => $product->id,
|
||||
'title' => $product->name,
|
||||
'description' => $product->copy,
|
||||
'link' => $product->getProductUrl(),
|
||||
'g:image_link' => $product->getImageUrl(),
|
||||
'g:availability' => 'in stock',
|
||||
'g:price' => "{$product->price} EUR",
|
||||
'g:brand' => 'MIVITA',
|
||||
'g:gtin' => $product->ean,
|
||||
'g:condition' => 'new',
|
||||
'g:custom_label_0' => $product->weight,
|
||||
'g:custom_label_1' => $product->contents_total,
|
||||
'g:custom_label_2' => $product->getUnitType(),
|
||||
'g:custom_label_3' => $product->contents_str,
|
||||
'g:custom_label_4' => $product->ingredients,
|
||||
'g:unit_pricing_measure' => $product->getBasePriceFormattedFullWith(false, false, null)
|
||||
]);
|
||||
}
|
||||
return $feed->generate();
|
||||
// Get the feed XML
|
||||
//$feedXml = $feed->toString();
|
||||
//return response($feedXml)->header('Content-Type', 'application/xml');
|
||||
}
|
||||
|
||||
// http://api.mivita.test/google/merchant/feed
|
||||
|
||||
}
|
||||
public function feed(){
|
||||
|
||||
$products = ModelsProduct::where('active', true)->whereJsonContains('show_on', '1')->orderBy('pos', 'DESC')->get();
|
||||
// Create feed object
|
||||
$feed = new Feed("mivita shop", "https://mivita.shop", "Bio Aloe Vera & Naturkosmetuk");
|
||||
|
||||
// Put products to the feed ($products - some data from database for example)
|
||||
foreach ($products as $product) {
|
||||
$item = new Product();
|
||||
|
||||
// Set common product properties
|
||||
$item->setId($product->id);
|
||||
$item->setTitle($product->name);
|
||||
$item->setDescription($product->copy);
|
||||
$item->setLink($product->getProductUrl());
|
||||
$item->setImage($product->getImageUrl());
|
||||
$item->setAvailability(Availability::IN_STOCK);
|
||||
|
||||
/*if ($product->isAvailable()) {
|
||||
$item->setAvailability(Availability::IN_STOCK);
|
||||
} else {
|
||||
$item->setAvailability(Availability::OUT_OF_STOCK);
|
||||
}*/
|
||||
$item->setPrice("{$product->price} EUR");
|
||||
//$item->setGoogleCategory($product->category_name);
|
||||
$item->setBrand('MIVITA');
|
||||
$item->setGtin($product->ean);
|
||||
$item->setCondition('new');
|
||||
|
||||
// Some additional properties
|
||||
//$item->setColor($product->color);
|
||||
//$item->setSize($product->size);
|
||||
|
||||
// Shipping info
|
||||
/*
|
||||
$shipping = new Shipping();
|
||||
$shipping->setCountry('US');
|
||||
$shipping->setRegion('CA, NSW, 03');
|
||||
$shipping->setPostalCode('94043');
|
||||
$shipping->setLocationId('21137');
|
||||
$shipping->setService('DHL');
|
||||
$shipping->setPrice('1300 USD');
|
||||
$item->setShipping($shipping);
|
||||
*/
|
||||
|
||||
// Set a custom shipping label and weight (optional)
|
||||
//$item->setShippingLabel('ups-ground');
|
||||
//$item->setShippingWeight('2.14');
|
||||
|
||||
// Set a custom label (optional)
|
||||
$item->setCustomLabel($product->weight, 'product_width');
|
||||
$item->setCustomLabel($product->contents_total, 'product_contents_total');
|
||||
$item->setCustomLabel($product->getUnitType(), 'product_contents_unit',);
|
||||
$item->setCustomLabel($product->contents_str, 'product_contents');
|
||||
$item->setCustomLabel($product->ingredients, 'product_ingredients');
|
||||
|
||||
$item->setCustomLabel($product->getBasePriceFormattedFullWith(false, false, null), 'product_base_pricing_unit');
|
||||
|
||||
|
||||
//$item->setCustomLabel('Some Label 2', 1);
|
||||
|
||||
// Add this product to the feed
|
||||
$feed->addProduct($item);
|
||||
}
|
||||
|
||||
// Here we get complete XML of the feed, that we could write to file or send directly
|
||||
$feedXml = $feed->build();
|
||||
print ($feedXml);
|
||||
}
|
||||
|
||||
// http://api.mivita.test/google/merchant/feed
|
||||
|
||||
}
|
||||
6
app/Http/Controllers/Api/KasController.php
Normal file → Executable file
6
app/Http/Controllers/Api/KasController.php
Normal file → Executable file
|
|
@ -13,7 +13,7 @@ class KasController extends Controller
|
|||
|
||||
// Logindaten
|
||||
private $kas_user = 'w017f6e4'; // KAS-Logon
|
||||
private $kas_pass = 'Medxiz-funteb-7dubdi'; // KAS-Passwort
|
||||
private $kas_pass = '7mMJUF4YSVWNpp39'; // KAS-Passwort
|
||||
private $session_lifetime = 600; // Gültigkeit des Tokens in Sek. bis zur neuen Authentifizierung
|
||||
private $session_update_lifetime = 'Y'; // bei N läuft die Session nach <$session_lifetime> Sekunden ab, bei Y verlängert sich die Session mit jeder Benutzung
|
||||
private $CredentialToken = false;
|
||||
|
|
@ -56,7 +56,7 @@ class KasController extends Controller
|
|||
}
|
||||
|
||||
// Fehler abfangen und ausgeben
|
||||
catch (\SoapFault $fault)
|
||||
catch (SoapFault $fault)
|
||||
{
|
||||
trigger_error(" Fehlernummer: {$fault->faultcode},
|
||||
Fehlermeldung: {$fault->faultstring},
|
||||
|
|
@ -88,7 +88,7 @@ class KasController extends Controller
|
|||
}
|
||||
|
||||
// Fehler abfangen und ausgeben
|
||||
catch (\SoapFault $fault)
|
||||
catch (SoapFault $fault)
|
||||
{
|
||||
trigger_error("Fehlernummer: {$fault->faultcode},
|
||||
Fehlermeldung: {$fault->faultstring},
|
||||
|
|
|
|||
201
app/Http/Controllers/Api/KasSLLController.php
Normal file → Executable file
201
app/Http/Controllers/Api/KasSLLController.php
Normal file → Executable file
|
|
@ -8,83 +8,154 @@ use App\Http\Controllers\Controller;
|
|||
class KasSLLController extends Controller
|
||||
{
|
||||
|
||||
private static $ssl_certificate_sni_csr = "";
|
||||
private static $ssl_certificate_sni_csr = "-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIC0DCCAbgCAQAwgYoxCzAJBgNVBAYTAkRFMQ4wDAYDVQQRDAU4Nzc1NTEPMA0G
|
||||
A1UECAwGQmF5ZXJuMRUwEwYDVQQHDAxLaXJjaGhhc2xhY2gxEzARBgNVBAkMCkxl
|
||||
aW5mZWxkIDIxFjAUBgNVBAoMDXJpd2EtdGVjIGUuSy4xFjAUBgNVBAMMDSoubWl2
|
||||
aXRhLmNhcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVOhtOTJBn
|
||||
5V9SmHmo/EawNiO0VwHOVnnrfnaPD2A1DeKqHmAfMTaybHaCfi+mufV8veemfY1j
|
||||
6rXq7RFU46SMBbFlfZqKS/3zb2d3yRT7OBU83PV5P8JXHrqEArlmKiOZcPoj86TT
|
||||
Abq5wwxjFXkePzJSdOdUN/Z1E1tI8ieUQC40tpMsRvf5XOzQZousXBT1P6F9Q2Fb
|
||||
UKEfiEBJ0wjnz74a73U7DebuYGEFPSjVjrkVB11+55y1MBkwg/6JIro+BlXorW6X
|
||||
aifb1PKFbTFQnlC4BAKyPHxNKWZCSHgw/C3A7fBQKHM1wVhZo2BZrumdE+X1FOSc
|
||||
WlN+M/+TyUybAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAJeDEZBjk9ITfZAzJ
|
||||
LMVIsu4Cuz2YZkZY8r+Wdd8E1k0lAdcht2xY/uL91NwXl/hUJiVo4uBUFnCogc/k
|
||||
dAxrRsrjiw8nHgfBgreGZj73S+tx00DUz1eP9uIVNzSO+aRMBHL8BvvLUR94KVSu
|
||||
aVhy8fJESdDiF5TwZR7jPIWoU0esI1cEebFG2kS/wTSuUWxLh1ZGGuEKFETfEpOK
|
||||
ooy0gUcHTP1NWo/vTDwdlf47t2vvZ/ZD0ursWXp6CNNZvwimHPxgSq8KKxLQyf5U
|
||||
S/UHogxC8PbOzTJI0DutkCZO0iUO8gTq0GXZHVqkqTCixfIFeuMuL0ZvXYJVhZXP
|
||||
4CBn5g==
|
||||
-----END CERTIFICATE REQUEST-----";
|
||||
private static $ssl_certificate_sni_key = "-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgohGr2e3ysw/Awvzh
|
||||
qkqDS4iQgRvWwNIYxTcPxpdcndGhRANCAASZjlV2bQbLQrOveMlYOowR3IlfND7z
|
||||
OxauFGabhvWSU1cg2w4U4bu/QXnDXfHHkcLp4M5WgHzX9Nw2m/abyJJ6
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVOhtOTJBn5V9S
|
||||
mHmo/EawNiO0VwHOVnnrfnaPD2A1DeKqHmAfMTaybHaCfi+mufV8veemfY1j6rXq
|
||||
7RFU46SMBbFlfZqKS/3zb2d3yRT7OBU83PV5P8JXHrqEArlmKiOZcPoj86TTAbq5
|
||||
wwxjFXkePzJSdOdUN/Z1E1tI8ieUQC40tpMsRvf5XOzQZousXBT1P6F9Q2FbUKEf
|
||||
iEBJ0wjnz74a73U7DebuYGEFPSjVjrkVB11+55y1MBkwg/6JIro+BlXorW6Xaifb
|
||||
1PKFbTFQnlC4BAKyPHxNKWZCSHgw/C3A7fBQKHM1wVhZo2BZrumdE+X1FOScWlN+
|
||||
M/+TyUybAgMBAAECggEAJ0hYj9AP44m6AiApRpbCdPiLhZmx3ANfrOJpi1dc2BqD
|
||||
pIzCePOXlnh+6fMV0Cn7uY60QFuksLzEjsdBXLtgQYvuGu1plSZT/5VAA4RnhYpJ
|
||||
7O+tnvFt00k/iCi/bWmCXY4kCvrEVNeLtALoa9znOVMhiBtGGiFxO3iQ+y7jxF6J
|
||||
49O99G8gPGjMm/BdFjnBpUZ+Z5ZGXvrKTZaQRDE5HXEM8dUTBXPL4+dMdfQIiyKZ
|
||||
pNklwkMjS4/LY6xDP16Wj25bSq5W9WSlTja/ZJ2eKqr6c7WxKP6TvjGh9FMkIUps
|
||||
Bl9BNKmgixgiHVq/4WwUSZ1PAEuGQJiptVdeJcgioQKBgQDDdNaRg6Z5yVk+UjXw
|
||||
DHJkUmquowijJUG/2seLYMFm1lkr9xbGvfGfnOSr79jim3haL/qichWh++QjeBsM
|
||||
fwBPMbRY+JNMHpaDpvHAI2YNqXP+rBr4pJnICrHoqIzVqxbDJ04LQZBRD10cTlFz
|
||||
+l+Ok60XTAX/wlKN96BnjuOVXQKBgQDDc2aoU37E4wPYNXcMLvoDv3+Zq3KCEMQD
|
||||
gtNgSbyd37Dw8n35TGWubFLsvYnPLBebB6wAgTPzvTpJmPTr7nKUJsd4rbfvuh+i
|
||||
vVhH/2xq70Pi1XqvQkmo+H1OJX+t2n/Hxr7TQGkqVI9eNfvW8UP+TGPjxGIw8Y0b
|
||||
6t8Ky6USVwKBgQCszV5qVh9Xqtj4zUwch5SW93qUHVWkj2rayP0ET62NUtKRmSmM
|
||||
2h+GAvr0u99fMR6tdZ+8AOr5RC7F4Qjg+mN2oLYWtuXbNWvSx0USnvk5+Oexb82E
|
||||
qFnBTxtNW77vpQxByz0nnHaQA+pI/UDsLZ5P+mXco/zlypKcKyKoi97PjQKBgDQV
|
||||
9+CZx6m+edLPhLc5eaUwDlgsaWqh/yqUXbJGVD6aUzQS22Fpa5uNAJhYdnZAYNYO
|
||||
uFa2F9s3rWXZnkOVmvFCWFwfp2n6Zt3eqb0eI41nz+aOT5CPEMQ33GTL93ekR/M8
|
||||
UrRHcP8347EOn9uLFjyZrPEQ773tUVaERAZDeO0nAoGAZXMhlmKmqTrM2jSb64ja
|
||||
pEddcEW2LuTvwQueOKUuSSwmCydKXkcgrYZ4EHyOgvVN9JZ5ZfW6ZathFipVEKdy
|
||||
diQ860kC4h++erAa8dvB1DUG5oldYYPiEKOyyyn+tNU298QcEkLrG1JcLuUXpfTg
|
||||
8dPIr+VpGomsvpwGTfJFjlE=
|
||||
-----END PRIVATE KEY-----";
|
||||
private static $ssl_certificate_sni_crt = "-----BEGIN CERTIFICATE-----
|
||||
MIIEpDCCBEqgAwIBAgIQVIm0T0SQ6D20YQxMaHEKbDAKBggqhkjOPQQDAjCBjzEL
|
||||
MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
|
||||
BxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5T
|
||||
ZWN0aWdvIEVDQyBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMB4X
|
||||
DTI0MDgwMTAwMDAwMFoXDTI1MDkwMTIzNTk1OVowGDEWMBQGA1UEAwwNKi5taXZp
|
||||
dGEuY2FyZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJmOVXZtBstCs694yVg6
|
||||
jBHciV80PvM7Fq4UZpuG9ZJTVyDbDhThu79BecNd8ceRwungzlaAfNf03Dab9pvI
|
||||
knqjggL8MIIC+DAfBgNVHSMEGDAWgBT2hQo7EYbhBH0Oqgss0u7MZHt7rjAdBgNV
|
||||
HQ4EFgQUVCkHH2AasJQFWFs63rdcb6BRvyowDgYDVR0PAQH/BAQDAgeAMAwGA1Ud
|
||||
EwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEkGA1UdIARC
|
||||
MEAwNAYLKwYBBAGyMQECAgcwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdv
|
||||
LmNvbS9DUFMwCAYGZ4EMAQIBMIGEBggrBgEFBQcBAQR4MHYwTwYIKwYBBQUHMAKG
|
||||
Q2h0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb0VDQ0RvbWFpblZhbGlkYXRp
|
||||
b25TZWN1cmVTZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl
|
||||
Y3RpZ28uY29tMCUGA1UdEQQeMByCDSoubWl2aXRhLmNhcmWCC21pdml0YS5jYXJl
|
||||
MIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgDd3Mo0ldfhFgXnlTL6x5/4PRxQ
|
||||
39sAOhQSdgosrLvIKgAAAZEMky0iAAAEAwBHMEUCICSH9TLHP8tqMyBTBpxF1+lw
|
||||
4wAnWf4E5pPJ6651S8P9AiEAkKqOQDaVdoFI1+jM28grXnG5o0vFLUwa0o49KYQ3
|
||||
k+sAdgAN4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAAAZEMkyzbAAAE
|
||||
AwBHMEUCIFJfJS4cojUm9nHQ1TVlxpFwOV7QwCj9MOfq0CCkVzsGAiEA8WQrE1ri
|
||||
kJkeIVPSgUVJpIz8TKef2aR+Ivzkzon52QIAdgAS8U40vVNyTIQGGcOPP3oT+Oe1
|
||||
YoeInG0wBYTr5YYmOgAAAZEMkyzBAAAEAwBHMEUCIQCH8/qTmCNea3FdBVk0c3Wu
|
||||
FrvYnoQlTQaaDS/zeTxSzwIge6VO5Aeor30Wu675zBYzNsIru5gXOTl4dteBMYnC
|
||||
0JswCgYIKoZIzj0EAwIDSAAwRQIhAKxmgpPqW6UAcWHCoWAPN673pBMxnCKn3vFq
|
||||
wUkhGrT7AiBDUsDuMhabsGlZ10X2GXcm+1mwxdMLSDYEWiwk5fUaNA==
|
||||
MIIGMTCCBRmgAwIBAgIRANRDAE1KIec3seJ9ed+Qy4UwDQYJKoZIhvcNAQELBQAw
|
||||
gY8xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
||||
BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE3MDUGA1UE
|
||||
AxMuU2VjdGlnbyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBD
|
||||
QTAeFw0yMjA3MTIwMDAwMDBaFw0yMzA4MTIyMzU5NTlaMBgxFjAUBgNVBAMMDSou
|
||||
bWl2aXRhLmNhcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVOhtO
|
||||
TJBn5V9SmHmo/EawNiO0VwHOVnnrfnaPD2A1DeKqHmAfMTaybHaCfi+mufV8veem
|
||||
fY1j6rXq7RFU46SMBbFlfZqKS/3zb2d3yRT7OBU83PV5P8JXHrqEArlmKiOZcPoj
|
||||
86TTAbq5wwxjFXkePzJSdOdUN/Z1E1tI8ieUQC40tpMsRvf5XOzQZousXBT1P6F9
|
||||
Q2FbUKEfiEBJ0wjnz74a73U7DebuYGEFPSjVjrkVB11+55y1MBkwg/6JIro+BlXo
|
||||
rW6Xaifb1PKFbTFQnlC4BAKyPHxNKWZCSHgw/C3A7fBQKHM1wVhZo2BZrumdE+X1
|
||||
FOScWlN+M/+TyUybAgMBAAGjggL8MIIC+DAfBgNVHSMEGDAWgBSNjF7EVK2K4Xfp
|
||||
m/mbBeG4AY1h4TAdBgNVHQ4EFgQUCS0Y1v7p19isO7cTuP3YrKVr2OcwDgYDVR0P
|
||||
AQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
||||
AQUFBwMCMEkGA1UdIARCMEAwNAYLKwYBBAGyMQECAgcwJTAjBggrBgEFBQcCARYX
|
||||
aHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQIBMIGEBggrBgEFBQcBAQR4
|
||||
MHYwTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1JT
|
||||
QURvbWFpblZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGG
|
||||
F2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMCUGA1UdEQQeMByCDSoubWl2aXRhLmNh
|
||||
cmWCC21pdml0YS5jYXJlMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdQCt9776
|
||||
fP8QyIudPZwePhhqtGcpXc+xDCTKhYY069yCigAAAYHxRovYAAAEAwBGMEQCIA3z
|
||||
UR5BFV7bwBcdRhS8mru20uq36DNz3ILivZh9yl4CAiAUjDxqZBW0Po/0Rm0gumI1
|
||||
VBZfqMSDiA7Cr1peGN8B8wB3AHoyjFTYty22IOo44FIe6YQWcDIThU070ivBOlej
|
||||
UutSAAABgfFGi6UAAAQDAEgwRgIhAN9s3/v2ygh1tfPQ8iX2dLZdOVxyuvC7bf15
|
||||
KP4NQyabAiEAz88hRBxRu3FifpLaYNjwxy1fRUc2luWfDdw+f31TOfoAdgDoPtDa
|
||||
PvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYHxRot7AAAEAwBHMEUCIQCQ
|
||||
RYqkeiRlStQacqUjuw5435NqNDqDlydAVYgywX05pwIgdcFtcLMGevO/KRyUeuWw
|
||||
8hYY6y1S7VYVdcFIuZVp5mswDQYJKoZIhvcNAQELBQADggEBAA0eEYSKcbgEPczo
|
||||
ABXpVsfbmaZqPhAKqcqKeGUcmFo7JHVPRUyck8RAF+SravyaHhilygU727QG4oUt
|
||||
riCewV39cKD2m7CO24WHe5+Fw8eslsJE+DBq/2WpLRJIGSWLl1r7WUELKQhqEYkr
|
||||
DCpkDXpG+lsDIfc5DC4dPLSWc9ezObsS4KEMCMDw+bj5GMGV6dHQZxAnbyqi71+v
|
||||
4+AOHpcYfe6v63w82M0YN5oTnaOukLVDgMXJ7WZP2op7atojB7DeM7k3+fj79kVJ
|
||||
jUGlvHLdN9jsczEZGGxL3w5oIjC4HMK1U5kyzEFWpc8ZLg+YPvF8w15lbhIXG94l
|
||||
JscmCFU=
|
||||
-----END CERTIFICATE-----";
|
||||
private static $ssl_certificate_sni_bundle = "-----BEGIN CERTIFICATE-----
|
||||
MIIDqDCCAy6gAwIBAgIRAPNkTmtuAFAjfglGvXvh9R0wCgYIKoZIzj0EAwMwgYgx
|
||||
CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz
|
||||
ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQD
|
||||
EyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEw
|
||||
MjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAkdCMRswGQYDVQQI
|
||||
ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT
|
||||
D1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMuU2VjdGlnbyBFQ0MgRG9tYWluIFZh
|
||||
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH
|
||||
A0IABHkYk8qfbZ5sVwAjBTcLXw9YWsTef1Wj6R7W2SUKiKAgSh16TwUwimNJE4xk
|
||||
IQeV/To14UrOkPAY9z2vaKb71EijggFuMIIBajAfBgNVHSMEGDAWgBQ64QmG1M8Z
|
||||
wpZ2dEl23OA1xmNjmjAdBgNVHQ4EFgQU9oUKOxGG4QR9DqoLLNLuzGR7e64wDgYD
|
||||
VR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEwUAYD
|
||||
VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz
|
||||
dEVDQ0NlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/
|
||||
BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVD
|
||||
Q0FkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1
|
||||
c3QuY29tMAoGCCqGSM49BAMDA2gAMGUCMEvnx3FcsVwJbZpCYF9z6fDWJtS1UVRs
|
||||
cS0chWBNKPFNpvDKdrdKRe+oAkr2jU+ubgIxAODheSr2XhcA7oz9HmedGdMhlrd9
|
||||
4ToKFbZl+/OnFFzqnvOhcjHvClECEQcKmc8fmA==
|
||||
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
|
||||
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
||||
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
||||
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
|
||||
MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV
|
||||
BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
|
||||
ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g
|
||||
VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N
|
||||
TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj
|
||||
eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E
|
||||
oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk
|
||||
Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY
|
||||
uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j
|
||||
BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb
|
||||
+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G
|
||||
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw
|
||||
CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0
|
||||
LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr
|
||||
BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv
|
||||
bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov
|
||||
L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H
|
||||
ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH
|
||||
7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi
|
||||
H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx
|
||||
RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv
|
||||
xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38
|
||||
sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL
|
||||
l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq
|
||||
6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY
|
||||
LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5
|
||||
yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K
|
||||
00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7
|
||||
MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7
|
||||
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
|
||||
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
|
||||
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4
|
||||
MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5
|
||||
MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO
|
||||
ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0
|
||||
aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL
|
||||
q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc
|
||||
JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA
|
||||
FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1
|
||||
xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI
|
||||
MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j
|
||||
b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG
|
||||
CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM
|
||||
BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy
|
||||
ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+
|
||||
FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV
|
||||
bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY
|
||||
CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf
|
||||
8qn0dNW44bOwgeThpWOjzOoEeJBuv/c=
|
||||
ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0
|
||||
aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgBJlFzYOw9sI
|
||||
s9CsVw127c0n00ytUINh4qogTQktZAnczomfzD2p7PbPwdzx07HWezcoEStH2jnG
|
||||
vDoZtF+mvX2do2NCtnbyqTsrkfjib9DsFiCQCT7i6HTJGLSR1GJk23+jBvGIGGqQ
|
||||
Ijy8/hPwhxR79uQfjtTkUcYRZ0YIUcuGFFQ/vDP+fmyc/xadGL1RjjWmp2bIcmfb
|
||||
IWax1Jt4A8BQOujM8Ny8nkz+rwWWNR9XWrf/zvk9tyy29lTdyOcSOk2uTIq3XJq0
|
||||
tyA9yn8iNK5+O2hmAUTnAU5GU5szYPeUvlM3kHND8zLDU+/bqv50TmnHa4xgk97E
|
||||
xwzf4TKuzJM7UXiVZ4vuPVb+DNBpDxsP8yUmazNt925H+nND5X4OpWaxKXwyhGNV
|
||||
icQNwZNUMBkTrNN9N6frXTpsNVzbQdcS2qlJC9/YgIoJk2KOtWbPJYjNhLixP6Q5
|
||||
D9kCnusSTJV882sFqV4Wg8y4Z+LoE53MW4LTTLPtW//e5XOsIzstAL81VXQJSdhJ
|
||||
WBp/kjbmUZIO8yZ9HE0XvMnsQybQv0FfQKlERPSZ51eHnlAfV1SoPv10Yy+xUGUJ
|
||||
5lhCLkMaTLTwJUdZ+gQek9QmRkpQgbLevni3/GcV4clXhB4PY9bpYrrWX1Uu6lzG
|
||||
KAgEJTm4Diup8kyXHAc/DVL17e8vgg8CAwEAAaOB8jCB7zAfBgNVHSMEGDAWgBSg
|
||||
EQojPpbxB+zirynvgqV/0DCktDAdBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rID
|
||||
ZsswDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAG
|
||||
BgRVHSAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29t
|
||||
L0FBQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggr
|
||||
BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUA
|
||||
A4IBAQAYh1HcdCE9nIrgJ7cz0C7M7PDmy14R3iJvm3WOnnL+5Nb+qh+cli3vA0p+
|
||||
rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+
|
||||
/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gA
|
||||
CiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1F
|
||||
zZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyA
|
||||
vGp4z7h/jnZymQyd/teRCBaho1+V
|
||||
-----END CERTIFICATE-----";
|
||||
|
||||
|
||||
|
|
|
|||
235
app/Http/Controllers/Api/PayoneController.php
Normal file → Executable file
235
app/Http/Controllers/Api/PayoneController.php
Normal file → Executable file
|
|
@ -2,26 +2,32 @@
|
|||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PaymentTransaction;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingPayment;
|
||||
use App\Services\MyLog;
|
||||
use App\Services\Payment;
|
||||
use App\Services\ShoppingUserService;
|
||||
use App\Services\Shop;
|
||||
use App\Services\Util;
|
||||
|
||||
|
||||
class PayoneController extends Controller
|
||||
{
|
||||
public function __construct() {}
|
||||
|
||||
public function paymentStatus()
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function paymentStatus(){
|
||||
|
||||
$data = \Request::all();
|
||||
// test para
|
||||
|
||||
/* $data = [
|
||||
/* $data = [
|
||||
'key' => '698fb2555f8b2efc74f60b2121421f45',
|
||||
'txaction' => 'paid',
|
||||
'clearingtype' => 'wlt',
|
||||
|
|
@ -34,114 +40,71 @@ class PayoneController extends Controller
|
|||
|
||||
*/
|
||||
|
||||
if (! isset($data['key']) || ! isset($data['param']) || ! isset($data['userid']) || ! isset($data['txid']) || ! isset($data['reference']) || ! isset($data['price'])) {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'error',
|
||||
'Error:2001 App\Http\Controllers\Api\PayoneController::paymentStatus parameter incomplete',
|
||||
$data
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
if(!isset($data['key']) || !isset($data['param']) || !isset($data['userid']) || !isset($data['txid']) || !isset($data['reference']) || !isset($data['price'])){
|
||||
\Log::channel('payone')->error('PaymentStatus: parameter incomplete: '.json_encode($data));
|
||||
echo "PaymentStatus: parameter incomplete:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
|
||||
if ($data['key'] != config('payone.defaults.key')) {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'error',
|
||||
'Error:2002 App\Http\Controllers\Api\PayoneController::paymentStatus Key error',
|
||||
$data
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
if($data['key'] != config('payone.defaults.key')) {
|
||||
\Log::channel('payone')->error('PaymentStatus: Key error: '.json_encode($data));
|
||||
echo "PaymentStatus: Key error:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
|
||||
$shopping_order = ShoppingOrder::find($data['param']);
|
||||
if (! $shopping_order) {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'error',
|
||||
'Error:2003 App\Http\Controllers\Api\PayoneController::paymentStatus ShoppingOrder not found:',
|
||||
$data
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
if(!$shopping_order){
|
||||
\Log::channel('payone')->error('PaymentStatus: ShoppingOrder not found: '.json_encode($data));
|
||||
echo "PaymentStatus: ShoppingOrder not found:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
|
||||
$shopping_payment = ShoppingPayment::where('reference', $data['reference'])->first();
|
||||
if (! $shopping_payment) {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'error',
|
||||
'Error:2004 App\Http\Controllers\Api\PayoneController::paymentStatus ShoppingPayment not found',
|
||||
$data
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
if(!$shopping_payment){
|
||||
\Log::channel('payone')->error('PaymentStatus: ShoppingPayment not found: '.json_encode($data));
|
||||
echo "PaymentStatus: ShoppingPayment not found:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
|
||||
if ($shopping_payment->shopping_order_id != $shopping_order->id) {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'error',
|
||||
'Error:2005 App\Http\Controllers\Api\PayoneController::paymentStatus ShoppingPayment no realation ShoppingOrder',
|
||||
$data
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
if($shopping_payment->shopping_order_id != $shopping_order->id){
|
||||
\Log::channel('payone')->error('PaymentStatus: ShoppingPayment no realation ShoppingOrder: '.json_encode($data));
|
||||
echo "PaymentStatus: ShoppingPayment no realation ShoppingOrder:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
|
||||
$price = number_format((round($data['price'], 2) * 100), 0, '.', '');
|
||||
if($data['key'] != config('payone.defaults.key')) {
|
||||
\Log::channel('payone')->error('PaymentStatus: Key error: '.json_encode($data));
|
||||
echo "PaymentStatus: ShoppingPayment no realation ShoppingOrder:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
|
||||
$price = number_format((round($data['price'],2) * 100), 0, '.', '');
|
||||
$price_amount = number_format($shopping_payment->amount, 0, '.', '');
|
||||
if ($price_amount != $price) {
|
||||
if($price_amount != $price){
|
||||
$data['shopping_payment-amount'] = $price_amount;
|
||||
$data['price-amount'] = $price;
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'error',
|
||||
'Error:2006 App\Http\Controllers\Api\PayoneController::paymentStatus Price error',
|
||||
$data
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
}
|
||||
/*
|
||||
* Payone sendet dieselbe txaction oft mehrfach (v. a. "appointed"). War der Status
|
||||
* bereits auf ShoppingPayment gespeichert, ist das ein Duplikat: TSOK, keine Doppel-Verarbeitung.
|
||||
* Ausnahme: erneutes "paid", obwohl die Bestellung noch nicht als bezahlt gefuehrt wird (Recovery).
|
||||
*/
|
||||
if ($shopping_payment->txaction == $data['txaction']) {
|
||||
if ($data['txaction'] === 'paid' && $shopping_order->txaction === 'paid') {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'notice',
|
||||
'App\Http\Controllers\Api\PayoneController::paymentStatus duplicate callback ignored (already paid)',
|
||||
$data,
|
||||
false
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
}
|
||||
|
||||
if (in_array($data['txaction'], ['appointed', 'failed', 'pending'], true)) {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'info',
|
||||
'App\Http\Controllers\Api\PayoneController::paymentStatus duplicate callback ignored (same txaction)',
|
||||
[
|
||||
'reference' => $data['reference'] ?? null,
|
||||
'param' => $data['param'] ?? null,
|
||||
'txaction' => $data['txaction'],
|
||||
'txid' => $data['txid'] ?? null,
|
||||
],
|
||||
false
|
||||
);
|
||||
echo 'TSOK';
|
||||
exit;
|
||||
}
|
||||
\Log::channel('payone')->error('PaymentStatus: Price error: '.json_encode($data));
|
||||
echo "PaymentStatus: Price error:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
|
||||
// create transaction
|
||||
/* TODO -- need this?
|
||||
|
||||
if($shopping_payment->txaction == $data['txaction']){
|
||||
\Log::channel('payone')->error('PaymentStatus: same txaction error: '.json_encode($data));
|
||||
echo "PaymentStatus: same txaction:";
|
||||
var_dump($data);
|
||||
die();
|
||||
}
|
||||
*/
|
||||
//create transaction
|
||||
PaymentTransaction::create([
|
||||
'shopping_payment_id' => $shopping_payment->id,
|
||||
'request' => 'transaction',
|
||||
|
|
@ -154,78 +117,30 @@ class PayoneController extends Controller
|
|||
'mode' => $data['mode'],
|
||||
]);
|
||||
|
||||
// Define txaction priority (higher number = higher priority)
|
||||
$txaction_priority = [
|
||||
'appointed' => 1,
|
||||
'pending' => 2,
|
||||
'failed' => 3,
|
||||
'paid' => 10, // highest priority - final state
|
||||
];
|
||||
|
||||
$current_priority = isset($txaction_priority[$shopping_order->txaction]) ? $txaction_priority[$shopping_order->txaction] : 0;
|
||||
$new_priority = isset($txaction_priority[$data['txaction']]) ? $txaction_priority[$data['txaction']] : 0;
|
||||
|
||||
// Only update txaction if new priority is higher than current
|
||||
if ($new_priority > $current_priority) {
|
||||
$shopping_order->txaction = $data['txaction'];
|
||||
$shopping_order->save();
|
||||
$shopping_payment->txaction = $data['txaction'];
|
||||
$shopping_payment->save();
|
||||
} else {
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'info',
|
||||
'App\Http\Controllers\Api\PayoneController::paymentStatus - txaction not updated (current: '.$shopping_order->txaction.' has higher/equal priority than new: '.$data['txaction'].')',
|
||||
$data,
|
||||
false
|
||||
);
|
||||
}
|
||||
$shopping_order->txaction = $data['txaction'];
|
||||
$shopping_order->save();
|
||||
$shopping_payment->txaction = $data['txaction'];
|
||||
$shopping_payment->save();
|
||||
|
||||
$send_link = false;
|
||||
$send_mail = true;
|
||||
if ($data['txaction'] === 'failed') {
|
||||
|
||||
|
||||
if($data['txaction'] === 'failed'){
|
||||
$shopping_order->setUserHistoryValue(['status' => 6]);
|
||||
Util::setInstanceStatusByPayment($shopping_payment, 5);
|
||||
|
||||
}
|
||||
if ($data['txaction'] === 'appointed') {
|
||||
if($data['txaction'] === 'appointed'){
|
||||
$shopping_order->setUserHistoryValue(['status' => 7]);
|
||||
ShoppingUserService::snycOrdersByShoppingOrder($shopping_order);
|
||||
Util::setInstanceStatusByPayment($shopping_payment, 4);
|
||||
Shop::userOrders();
|
||||
}
|
||||
|
||||
if ($data['txaction'] === 'paid') {
|
||||
// Use DB transaction and row locking to prevent race conditions
|
||||
\DB::beginTransaction();
|
||||
try {
|
||||
// Lock the shopping order row to prevent concurrent processing
|
||||
$locked_order = ShoppingOrder::where('id', $shopping_order->id)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
// Double-check if payment was already processed
|
||||
if (! $locked_order->paid) {
|
||||
$send_link = Payment::paymentStatusPaidAction($locked_order, true, $shopping_payment);
|
||||
\DB::commit();
|
||||
} else {
|
||||
$send_mail = false;
|
||||
\DB::commit();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollBack();
|
||||
MyLog::writeLog(
|
||||
'payone',
|
||||
'error',
|
||||
'Error:2008 App\Http\Controllers\Api\PayoneController::paymentStatus Transaction failed',
|
||||
['error' => $e->getMessage(), 'data' => $data]
|
||||
);
|
||||
throw $e;
|
||||
}
|
||||
if($data['txaction'] === 'paid'){
|
||||
$send_link = Payment::paymentStatusPaidAction($shopping_order, true);
|
||||
}
|
||||
|
||||
$data['send_link'] = $send_link;
|
||||
if ($send_mail) {
|
||||
Payment::paymentStatusSendMail($shopping_order, $shopping_payment, $data);
|
||||
}
|
||||
echo 'TSOK';
|
||||
Payment::paymentStatusSendMail($shopping_order, $shopping_payment, $data);
|
||||
print("TSOK");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
404
app/Http/Controllers/Api/ShoppingUserController.php
Normal file → Executable file
404
app/Http/Controllers/Api/ShoppingUserController.php
Normal file → Executable file
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Mail\MailCheckout;
|
||||
use App\Models\Country;
|
||||
use App\Models\Product;
|
||||
|
|
@ -11,23 +10,28 @@ use App\Models\ShoppingOrder;
|
|||
use App\Models\ShoppingOrderItem;
|
||||
use App\Models\ShoppingUser;
|
||||
use App\Services\CustomerPriority;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use PHPUnit\Framework\Constraint\Count;
|
||||
use Yard;
|
||||
|
||||
|
||||
class ShoppingUserController extends Controller
|
||||
{
|
||||
// protected static API_MAIL = 'api.thomas.krummel@gmail.com';
|
||||
// protected static API_PASS = 'UF(Q<9knap!ev3vH?5~!b8DP';
|
||||
|
||||
//protected static API_MAIL = 'api.thomas.krummel@gmail.com';
|
||||
//protected static API_PASS = 'UF(Q<9knap!ev3vH?5~!b8DP';
|
||||
|
||||
|
||||
protected $successStatus = 200;
|
||||
protected $member_id = 3; //service@aloe-vera.bio
|
||||
|
||||
protected $member_id = 3; // service@aloe-vera.bio
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* wp_order_numbers[1234, 1234]
|
||||
* @param Request $request
|
||||
* wp_order_numbers[1234, 1234]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function status(Request $request)
|
||||
|
|
@ -37,23 +41,23 @@ class ShoppingUserController extends Controller
|
|||
'wp_order_numbers' => 'required',
|
||||
]);
|
||||
|
||||
if (! is_array($request->wp_order_numbers)) {
|
||||
if(!is_array($request->wp_order_numbers)){
|
||||
$wp_order_numbers = json_decode($request->wp_order_numbers);
|
||||
|
||||
} else {
|
||||
$wp_order_numbers = $request->wp_order_numbers;
|
||||
}else{
|
||||
$wp_order_numbers = $request->wp_order_numbers;
|
||||
}
|
||||
|
||||
if (! $wp_order_numbers || ! is_array($wp_order_numbers)) {
|
||||
if(!$wp_order_numbers || !is_array($wp_order_numbers)){
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'wp_order_numbers need as json [1234, 1234] ',
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
|
||||
$status = [];
|
||||
foreach ($wp_order_numbers as $wp_order_number) {
|
||||
foreach ($wp_order_numbers as $wp_order_number){
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $wp_order_number)->first();
|
||||
$status[] = [
|
||||
'wp_order_number' => $wp_order_number,
|
||||
|
|
@ -66,14 +70,14 @@ class ShoppingUserController extends Controller
|
|||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $status,
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* wp_order_number [1234]
|
||||
* @param Request $request
|
||||
* wp_order_number [1234]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function cancel(Request $request)
|
||||
|
|
@ -82,31 +86,31 @@ class ShoppingUserController extends Controller
|
|||
'wp_order_number' => 'required|int',
|
||||
]);
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $request->wp_order_number)->first();
|
||||
if (! $shopping_user) {
|
||||
if (!$shopping_user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' not found',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' not found',
|
||||
'order' => false,
|
||||
'status' => false,
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
if (! $shopping_user->shopping_order) {
|
||||
if(!$shopping_user->shopping_order){
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' has no order',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' has no order',
|
||||
'order' => false,
|
||||
'status' => $shopping_user->getAPIShippedType(),
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
if ($shopping_user->shopping_order->shipped > 0) {
|
||||
if($shopping_user->shopping_order->shipped > 0){
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' can not cancel',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' can not cancel',
|
||||
'order' => true,
|
||||
'status' => $shopping_user->getAPIShippedType(),
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
|
||||
|
|
@ -115,17 +119,18 @@ class ShoppingUserController extends Controller
|
|||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' is cancel',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' is cancel',
|
||||
'order' => true,
|
||||
'status' => $shopping_user->getAPIShippedType(),
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* wp_order_number [1234]
|
||||
* @param Request $request
|
||||
* wp_order_number [1234]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function open(Request $request)
|
||||
|
|
@ -134,31 +139,31 @@ class ShoppingUserController extends Controller
|
|||
'wp_order_number' => 'required|int',
|
||||
]);
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $request->wp_order_number)->first();
|
||||
if (! $shopping_user) {
|
||||
if (!$shopping_user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' not found',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' not found',
|
||||
'order' => false,
|
||||
'status' => false,
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
if (! $shopping_user->shopping_order) {
|
||||
if(!$shopping_user->shopping_order){
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' has no order',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' has no order',
|
||||
'order' => false,
|
||||
'status' => $shopping_user->getAPIShippedType(),
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
if ($shopping_user->shopping_order->shipped !== 10) {
|
||||
if($shopping_user->shopping_order->shipped !== 10){
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' can not open',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' can not open',
|
||||
'order' => true,
|
||||
'status' => $shopping_user->getAPIShippedType(),
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
|
||||
|
|
@ -167,44 +172,45 @@ class ShoppingUserController extends Controller
|
|||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' is open',
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' is open',
|
||||
'order' => true,
|
||||
'status' => $shopping_user->getAPIShippedType(),
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* wp_order_numbers [1234, 1234]
|
||||
* @param Request $request
|
||||
* wp_order_numbers [1234, 1234]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request)
|
||||
{
|
||||
// $this->member_id = auth()->user()->m_sponsor;
|
||||
//$this->member_id = auth()->user()->m_sponsor;
|
||||
$request->validate([
|
||||
'wp_order_numbers' => 'required',
|
||||
]);
|
||||
|
||||
if (! is_array($request->wp_order_numbers)) {
|
||||
if(!is_array($request->wp_order_numbers)){
|
||||
$wp_order_numbers = json_decode($request->wp_order_numbers);
|
||||
|
||||
} else {
|
||||
$wp_order_numbers = $request->wp_order_numbers;
|
||||
}else{
|
||||
$wp_order_numbers = $request->wp_order_numbers;
|
||||
}
|
||||
|
||||
if (! $wp_order_numbers || ! is_array($wp_order_numbers)) {
|
||||
if(!$wp_order_numbers || !is_array($wp_order_numbers)){
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'wp_order_numbers need as json [1234, 1234] ',
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
$data = [];
|
||||
|
||||
foreach ($wp_order_numbers as $wp_order_number) {
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $wp_order_number)->first();
|
||||
foreach ($wp_order_numbers as $wp_order_number){
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $wp_order_number)->first();
|
||||
$user = false;
|
||||
$order = false;
|
||||
if ($shopping_user) {
|
||||
|
|
@ -214,21 +220,21 @@ class ShoppingUserController extends Controller
|
|||
$data[] = [
|
||||
'wp_order_number' => $wp_order_number,
|
||||
'user' => $user,
|
||||
'order' => $order,
|
||||
'order' => $order,
|
||||
'customer_number' => $shopping_user ? $shopping_user->number : false,
|
||||
'member_email' => ($shopping_user && $shopping_user->member) ? $shopping_user->member->email : false,
|
||||
'status' => $shopping_user ? $shopping_user->getAPIShippedType() : false, ];
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $data,
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
|
|
@ -248,20 +254,19 @@ class ShoppingUserController extends Controller
|
|||
$this->member_id = auth()->user()->m_sponsor;
|
||||
|
||||
$data = $this->prepareForStore($request->all());
|
||||
$data['member_id'] = $this->member_id;
|
||||
$data['member_id'] = $this->member_id ;
|
||||
$data['number'] = ShoppingUser::max('number') + 1;
|
||||
$data['mode'] = $request->mode ? $request->mode : 'live';
|
||||
$data['is_from'] = 'extern';
|
||||
$data['is_for'] = 'ot-member';
|
||||
$data['is_for'] = 'ot';
|
||||
|
||||
$shopping_user = ShoppingUser::create($data);
|
||||
|
||||
// Kundenhoheit prüfen
|
||||
//Kundenhoheit prüfen
|
||||
$priority = CustomerPriority::checkOne($shopping_user, true, false, true);
|
||||
\App\Services\Shop::newUserOrder($shopping_user->number);
|
||||
// exists //like //update
|
||||
//exists //like //update
|
||||
$user = $this->prepareForShow($shopping_user);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
|
|
@ -271,12 +276,13 @@ class ShoppingUserController extends Controller
|
|||
'customer_number' => $shopping_user->number,
|
||||
'member_email' => ($shopping_user && $shopping_user->member) ? $shopping_user->member->email : false,
|
||||
],
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(Request $request)
|
||||
|
|
@ -285,42 +291,40 @@ class ShoppingUserController extends Controller
|
|||
'wp_order_number' => 'required|int',
|
||||
]);
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $request->wp_order_number)->first();
|
||||
if (! $shopping_user) {
|
||||
if (!$shopping_user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' not found',
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' not found',
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
|
||||
$data = $this->prepareForUpdate($request->all());
|
||||
// Kundenhoheit prüfen
|
||||
//Kundenhoheit prüfen
|
||||
$priority = CustomerPriority::checkChangeOne($shopping_user, $data, true);
|
||||
$updated = $shopping_user->fill($data)->save();
|
||||
\App\Services\Shop::newUserOrder($shopping_user->number);
|
||||
|
||||
if ($updated) {
|
||||
if ($updated){
|
||||
$user = $this->prepareForShow($shopping_user);
|
||||
$order = $this->prepareForShowOrder($shopping_user->shopping_order);
|
||||
|
||||
return response()->json([
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'wp_order_number' => $shopping_user->wp_order_number,
|
||||
'user' => $user,
|
||||
'order' => $order,
|
||||
'order' => $order,
|
||||
'customer_priority' => $priority,
|
||||
'customer_number' => $shopping_user ? $shopping_user->number : false,
|
||||
'member_email' => ($shopping_user && $shopping_user->member) ? $shopping_user->member->email : false,
|
||||
'status' => $shopping_user ? $shopping_user->getAPIShippedType() : false,
|
||||
],
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 200);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry could not be updated',
|
||||
'message' => 'Entry could not be updated'
|
||||
], 500);
|
||||
}
|
||||
|
||||
|
|
@ -331,27 +335,28 @@ class ShoppingUserController extends Controller
|
|||
'wp_order' => 'required',
|
||||
]);
|
||||
|
||||
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $request->wp_order_number)->first();
|
||||
if (! $shopping_user) {
|
||||
if (!$shopping_user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' not found',
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' not found',
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
|
||||
if ($shopping_user->shopping_order) {
|
||||
if($shopping_user->shopping_order){
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Order with wp_order_number '.$request->wp_order_number.' exists',
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'message' => 'Order with wp_order_number ' . $request->wp_order_number . ' exists',
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
if (! is_array($request->wp_order)) {
|
||||
if(!is_array($request->wp_order)){
|
||||
$wp_order = json_decode($request->wp_order);
|
||||
|
||||
} else {
|
||||
$wp_order = $request->wp_order;
|
||||
}else{
|
||||
$wp_order = $request->wp_order;
|
||||
}
|
||||
|
||||
$wp_invoice_path = isset($request->wp_invoice_path) ? $request->wp_invoice_path : null;
|
||||
|
|
@ -365,10 +370,9 @@ class ShoppingUserController extends Controller
|
|||
|
||||
$wp_order = $this->prepareOrder($wp_order, $shopping_user, $wp_invoice_path, $api_notice);
|
||||
|
||||
if ($wp_order) {
|
||||
if ($wp_order){
|
||||
$user = $this->prepareForShow($shopping_user);
|
||||
$order = $this->prepareForShowOrder($shopping_user->shopping_order);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
|
|
@ -383,175 +387,166 @@ class ShoppingUserController extends Controller
|
|||
'member_email' => ($shopping_user && $shopping_user->member) ? $shopping_user->member->email : false,
|
||||
'status' => $shopping_user->getAPIShippedType(),
|
||||
],
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 200);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Order could not be stored',
|
||||
'message' => 'Order could not be stored'
|
||||
], 500);
|
||||
}
|
||||
|
||||
public function delete(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'wp_order_number' => 'required|int',
|
||||
]);
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $request->wp_order_number)->where('mode', '=', 'dev')->first();
|
||||
if (! $shopping_user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number '.$request->wp_order_number.' not found or mode != dev',
|
||||
'time' => Carbon::now()->toDateTimeString(),
|
||||
], 400);
|
||||
}
|
||||
$shopping_order = $shopping_user->shopping_order;
|
||||
if ($shopping_order) {
|
||||
foreach ($shopping_order->shopping_order_items as $shopping_order_item) {
|
||||
$shopping_order_item->delete();
|
||||
}
|
||||
$shopping_order->delete();
|
||||
}
|
||||
$shopping_user->wp_order_number = time();
|
||||
$shopping_user->save();
|
||||
if ($shopping_user->delete()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
{
|
||||
$request->validate([
|
||||
'wp_order_number' => 'required|int',
|
||||
]);
|
||||
$shopping_user = ShoppingUser::where('wp_order_number', '=', $request->wp_order_number)->where('mode', '=', 'dev')->first();
|
||||
if (!$shopping_user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry with wp_order_number ' . $request->wp_order_number . ' not found or mode != dev',
|
||||
'time' => Carbon::now()->toDateTimeString()
|
||||
], 400);
|
||||
}
|
||||
$shopping_order = $shopping_user->shopping_order;
|
||||
if($shopping_order){
|
||||
foreach ($shopping_order->shopping_order_items as $shopping_order_item){
|
||||
$shopping_order_item->delete();
|
||||
}
|
||||
$shopping_order->delete();
|
||||
}
|
||||
$shopping_user->wp_order_number = time();
|
||||
$shopping_user->save();
|
||||
if ($shopping_user->delete()) {
|
||||
return response()->json([
|
||||
'success' => true
|
||||
]);
|
||||
}
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry could not be deleted'
|
||||
], 500);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Entry could not be deleted',
|
||||
], 500);
|
||||
}
|
||||
private function prepareForShow($shopping_user){
|
||||
|
||||
private function prepareForShow($shopping_user)
|
||||
{
|
||||
|
||||
if (! $shopping_user) {
|
||||
if(!$shopping_user){
|
||||
return false;
|
||||
}
|
||||
|
||||
$shopping_user_data = $shopping_user->toArray();
|
||||
$needs = ['wp_order_number', 'wp_order_date', 'billing_company', 'billing_firstname', 'billing_lastname', 'billing_address', 'billing_address_2', 'billing_zipcode', 'billing_city', 'billing_phone', 'billing_email',
|
||||
'same_as_billing', 'shipping_company', 'shipping_firstname', 'shipping_lastname', 'shipping_address', 'shipping_address_2', 'shipping_zipcode', 'shipping_city', 'shipping_phone',
|
||||
'created_at', 'updated_at', 'user_deleted_at']; // 'has_buyed', 'subscribed',
|
||||
'created_at', 'updated_at', 'user_deleted_at']; //'has_buyed', 'subscribed',
|
||||
|
||||
// $salutation = array('mr' => 1, 'ms' => 2);
|
||||
//$salutation = array('mr' => 1, 'ms' => 2);
|
||||
$ret = [];
|
||||
foreach ($shopping_user_data as $key => $value) {
|
||||
foreach ($shopping_user_data as $key=>$value){
|
||||
|
||||
if ($key === 'billing_country_id') {
|
||||
if($key === 'billing_country_id'){
|
||||
$ret['billing_country_code'] = $shopping_user->billing_country_id ? $shopping_user->billing_country->code : null;
|
||||
}
|
||||
if ($key === 'shipping_country_id') {
|
||||
if($key === 'shipping_country_id'){
|
||||
$ret['shipping_country_code'] = $shopping_user->shipping_country_id ? $shopping_user->shipping_country->code : null;
|
||||
}
|
||||
if ($key === 'billing_salutation') {
|
||||
if($key === 'billing_salutation'){
|
||||
$ret['billing_salutation'] = $shopping_user->billing_salutation === 'ms' ? 2 : 1;
|
||||
}
|
||||
if ($key === 'shipping_salutation') {
|
||||
if($key === 'shipping_salutation'){
|
||||
$ret['shipping_salutation'] = $shopping_user->shipping_salutation === 'ms' ? 2 : 1;
|
||||
}
|
||||
|
||||
if (in_array($key, $needs)) {
|
||||
if(in_array($key, $needs)){
|
||||
$ret[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function prepareForShowOrder($shopping_order)
|
||||
{
|
||||
private function prepareForShowOrder($shopping_order){
|
||||
|
||||
if (! $shopping_order) {
|
||||
if(!$shopping_order){
|
||||
return false;
|
||||
}
|
||||
$ret = [
|
||||
'country' => isset($shopping_order->shipping_country->country->code) ? $shopping_order->shipping_country->country->code : '',
|
||||
'wp_invoice_path' => $shopping_order->wp_invoice_path,
|
||||
'total' => ($shopping_order->total * 100),
|
||||
'shipping' => ($shopping_order->shipping * 100),
|
||||
'total_net' => ($shopping_order->subtotal * 100),
|
||||
'tax_rate' => ($shopping_order->tax_rate * 100),
|
||||
'tax' => ($shopping_order->tax * 100),
|
||||
'total_with_shipping' => ($shopping_order->total_shipping * 100),
|
||||
'total' => ($shopping_order->total*100),
|
||||
'shipping' => ($shopping_order->shipping*100),
|
||||
'total_net' => ($shopping_order->subtotal*100),
|
||||
'tax_rate' => ($shopping_order->tax_rate*100),
|
||||
'tax' => ($shopping_order->tax*100),
|
||||
'total_with_shipping' => ($shopping_order->total_shipping*100),
|
||||
'weight' => $shopping_order->weight,
|
||||
];
|
||||
$ret['items'] = [];
|
||||
foreach ($shopping_order->shopping_order_items as $item) {
|
||||
foreach ($shopping_order->shopping_order_items as $item){
|
||||
$ret['items'][] = [
|
||||
'article' => $item->product->wp_number,
|
||||
'name' => $item->product->getLang('name'),
|
||||
'qty' => $item->qty,
|
||||
'price' => ($item->price * 100),
|
||||
'article' => $item->product->wp_number,
|
||||
'name' => $item->product->name,
|
||||
'qty' => $item->qty,
|
||||
'price' => ($item->price * 100),
|
||||
];
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function prepareForUpdate($data)
|
||||
{
|
||||
private function prepareForUpdate($data){
|
||||
|
||||
// $salutation = array(1 => 'mr', 2 => 'ms', 3=>null);
|
||||
//$salutation = array(1 => 'mr', 2 => 'ms', 3=>null);
|
||||
|
||||
if (isset($data['billing_salutation'])) {
|
||||
$data['billing_salutation'] = (int) $data['billing_salutation'];
|
||||
if(isset($data['billing_salutation'])){
|
||||
$data['billing_salutation'] = (int) $data['billing_salutation'];
|
||||
$data['billing_salutation'] = $data['billing_salutation'] == 2 ? 'ms' : 'mr';
|
||||
}
|
||||
if (isset($data['shipping_salutation'])) {
|
||||
$data['shipping_salutation'] = (int) $data['shipping_salutation'];
|
||||
$data['shipping_salutation'] = $data['shipping_salutation'] == 2 ? 'ms' : 'mr';
|
||||
if(isset($data['shipping_salutation'])){
|
||||
$data['shipping_salutation'] = (int) $data['shipping_salutation'];
|
||||
$data['shipping_salutation'] = $data['shipping_salutation'] == 2 ? 'ms' : 'mr';
|
||||
}
|
||||
|
||||
$ret = [];
|
||||
$needs = ['billing_salutation', 'billing_company', 'billing_firstname', 'billing_lastname', 'billing_address', 'billing_address_2', 'billing_zipcode', 'billing_city', 'billing_phone', 'billing_email', 'same_as_billing',
|
||||
$needs = [ 'billing_salutation', 'billing_company', 'billing_firstname', 'billing_lastname', 'billing_address', 'billing_address_2', 'billing_zipcode', 'billing_city', 'billing_phone', 'billing_email', 'same_as_billing',
|
||||
'shipping_salutation', 'shipping_company', 'shipping_firstname', 'shipping_lastname', 'shipping_address', 'shipping_address_2', 'shipping_zipcode', 'shipping_city', 'shipping_phone'];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($key === 'billing_country_code' && isset($data['billing_country_code'])) {
|
||||
$ret['billing_country_id'] = Country::getCountryIdByCodeOrOne($data['billing_country_code']);
|
||||
foreach ($data as $key=>$value){
|
||||
if($key === 'billing_country_code' && isset($data['billing_country_code'])) {
|
||||
$ret['billing_country_id'] = Country::getCountryIdByCodeOrOne($data['billing_country_code']);
|
||||
}
|
||||
if ($key === 'shipping_country_code' && isset($data['shipping_country_code'])) {
|
||||
if($key === 'shipping_country_code' && isset($data['shipping_country_code']) ) {
|
||||
$ret['shipping_country_id'] = Country::getCountryIdByCodeOrOne($data['shipping_country_code']);
|
||||
}
|
||||
if ($key === 'billing_phone') {
|
||||
if($key === 'billing_phone') {
|
||||
$ret['billing_phone'] = strlen($data['billing_phone']) <= 3 ? '' : $data['billing_phone'];
|
||||
}
|
||||
if ($key === 'shipping_phone') {
|
||||
if($key === 'shipping_phone') {
|
||||
$ret['shipping_phone'] = strlen($data['shipping_phone']) <= 3 ? '' : $data['shipping_phone'];
|
||||
}
|
||||
if (in_array($key, $needs)) {
|
||||
if(in_array($key, $needs)){
|
||||
$ret[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function prepareForStore($data)
|
||||
{
|
||||
private function prepareForStore($data){
|
||||
|
||||
// $salutation = array(1 => 'mr', 2 => 'ms', 3=>null);
|
||||
if (isset($data['billing_salutation'])) {
|
||||
$data['billing_salutation'] = (int) $data['billing_salutation'];
|
||||
//$salutation = array(1 => 'mr', 2 => 'ms', 3=>null);
|
||||
if(isset($data['billing_salutation'])){
|
||||
$data['billing_salutation'] = (int) $data['billing_salutation'];
|
||||
$data['billing_salutation'] = $data['billing_salutation'] == 2 ? 'ms' : 'mr';
|
||||
}
|
||||
if (isset($data['shipping_salutation'])) {
|
||||
$data['shipping_salutation'] = (int) $data['shipping_salutation'];
|
||||
$data['shipping_salutation'] = $data['shipping_salutation'] == 2 ? 'ms' : 'mr';
|
||||
if(isset($data['shipping_salutation'])){
|
||||
$data['shipping_salutation'] = (int) $data['shipping_salutation'];
|
||||
$data['shipping_salutation'] = $data['shipping_salutation'] == 2 ? 'ms' : 'mr';
|
||||
}
|
||||
$ret = [];
|
||||
$needs = ['billing_salutation', 'billing_company', 'billing_firstname', 'billing_lastname', 'billing_address', 'billing_address_2', 'billing_zipcode', 'billing_city', 'billing_country_id', 'billing_phone', 'billing_email',
|
||||
$needs = [ 'billing_salutation', 'billing_company', 'billing_firstname', 'billing_lastname', 'billing_address', 'billing_address_2', 'billing_zipcode', 'billing_city', 'billing_country_id', 'billing_phone', 'billing_email',
|
||||
'shipping_salutation', 'shipping_company', 'shipping_firstname', 'shipping_lastname', 'shipping_address', 'shipping_address_2', 'shipping_zipcode', 'shipping_city', 'shipping_country_id', 'shipping_phone',
|
||||
'same_as_billing', // 'has_buyed', 'subscribed',
|
||||
'same_as_billing', //'has_buyed', 'subscribed',
|
||||
'wp_order_number', 'wp_order_date'];
|
||||
|
||||
foreach ($needs as $need) {
|
||||
foreach ($needs as $need){
|
||||
|
||||
$ret[$need] = isset($data[$need]) ? $data[$need] : null;
|
||||
if ($need === 'billing_country_id') {
|
||||
|
|
@ -570,37 +565,35 @@ class ShoppingUserController extends Controller
|
|||
$ret['wp_order_date'] = Carbon::parse($ret['wp_order_date'])->toDateTimeString();
|
||||
}
|
||||
if ($need === 'same_as_billing') {
|
||||
$ret['same_as_billing'] = isset($data['same_as_billing']) ? $data['same_as_billing'] : true;
|
||||
$ret['same_as_billing'] = isset($data['same_as_billing']) ? $data['same_as_billing'] : true;
|
||||
}
|
||||
}
|
||||
$ret['has_buyed'] = true;
|
||||
$ret['subscribed'] = false;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function prepareOrder($wp_shopping_order, $shopping_user, $wp_invoice_path, $api_notice)
|
||||
{
|
||||
private function prepareOrder($wp_shopping_order, $shopping_user, $wp_invoice_path, $api_notice){
|
||||
Yard::instance('shopping')->destroy();
|
||||
$ret = [];
|
||||
|
||||
if (is_array($wp_shopping_order)) {
|
||||
if(is_array($wp_shopping_order)){
|
||||
foreach ($wp_shopping_order as $order) {
|
||||
// $object = json_decode(json_encode($order), FALSE);
|
||||
//$object = json_decode(json_encode($order), FALSE);
|
||||
$order = (object) $order;
|
||||
$error = [];
|
||||
if (! isset($order->article) || ! isset($order->qty) || ! isset($order->price)) {
|
||||
$error[] = 'article parameter is missing';
|
||||
if (!isset($order->article) || !isset($order->qty) || !isset($order->price)) {
|
||||
$error[] = "article parameter is missing";
|
||||
} else {
|
||||
|
||||
$product = Product::whereWpNumber($order->article)->first();
|
||||
if (! $product) {
|
||||
$error[] = 'article not found';
|
||||
if (!$product) {
|
||||
$error[] = "article not found";
|
||||
} else {
|
||||
if ($order->price != ($product->price * 100)) {
|
||||
$error[] = 'different price: '.($product->price * 100);
|
||||
$error[] = "different price: " . ($product->price * 100);
|
||||
}
|
||||
$cartItem = Yard::instance('shopping')->add($product->id, $product->getLang('name'), (int) $order->qty, $product->price, false, false, ['image' => [], 'slug' => $product->slug, 'weight' => $product->weight, 'points' => $product->points, 'no_commission' => $product->no_commission, 'no_free_shipping' => $product->no_free_shipping, 'show_on' => $product->show_on]);
|
||||
$cartItem = Yard::instance('shopping')->add($product->id, $product->name, (int) $order->qty, $product->price, false, false, ['image' => [], 'slug' => $product->slug, 'weight' => $product->weight, 'points' => $product->points, 'no_commission' => $product->no_commission]);
|
||||
Yard::setTax($cartItem->rowId, $product->getTaxWith());
|
||||
}
|
||||
}
|
||||
|
|
@ -609,7 +602,7 @@ class ShoppingUserController extends Controller
|
|||
}
|
||||
|
||||
$ShippingCountry = ShippingCountry::whereCountryId($shopping_user->shipping_country_id)->first();
|
||||
if ($ShippingCountry) {
|
||||
if($ShippingCountry){
|
||||
Yard::instance('shopping')->setShippingCountryWithPrice($ShippingCountry->id);
|
||||
}
|
||||
$shopping_order = $this->makeShoppingOrder($shopping_user, $wp_invoice_path, $api_notice);
|
||||
|
|
@ -618,18 +611,15 @@ class ShoppingUserController extends Controller
|
|||
$shopping_user->shopping_order = $shopping_order;
|
||||
Yard::instance('shopping')->destroy();
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function makeShoppingOrder($shopping_user, $wp_invoice_path, $api_notice)
|
||||
{
|
||||
|
||||
private function makeShoppingOrder($shopping_user, $wp_invoice_path, $api_notice){
|
||||
|
||||
$data = [
|
||||
'shopping_user_id' => $shopping_user->id,
|
||||
'auth_user_id' => $shopping_user->auth_user_id,
|
||||
'country_id' => Yard::instance('shopping')->getShippingCountryId(),
|
||||
'language' => \App::getLocale(),
|
||||
'user_shop_id' => auth()->user()->user_sponsor->shop->id,
|
||||
'payment_for' => 7,
|
||||
'member_id' => $shopping_user->member_id,
|
||||
|
|
@ -650,22 +640,23 @@ class ShoppingUserController extends Controller
|
|||
'mode' => $shopping_user->mode,
|
||||
];
|
||||
$shopping_order = $shopping_user->shopping_order;
|
||||
if ($shopping_order) {
|
||||
if($shopping_order){
|
||||
$shopping_order->fill($data);
|
||||
$shopping_order->save();
|
||||
} else {
|
||||
$shopping_order = ShoppingOrder::create($data);
|
||||
}else{
|
||||
$shopping_order= ShoppingOrder::create($data);
|
||||
}
|
||||
$items = Yard::instance('shopping')->content();
|
||||
|
||||
$shopping_order->shopping_order_items()->each(function ($model) use ($items, $shopping_order) {
|
||||
|
||||
$shopping_order->shopping_order_items()->each(function($model) use ($items, $shopping_order) {
|
||||
foreach ($items as $item) {
|
||||
$price_net = Yard::instance('shopping')->rowPriceNet($item, 2, '.', '');
|
||||
$tax = $item->price - $price_net;
|
||||
if ($model->row_id === $item->rowId) {
|
||||
$model->fill([
|
||||
'shopping_order_id' => $shopping_order->id,
|
||||
'row_id' => $item->rowId,
|
||||
'row_id' => $item->rowId,
|
||||
'product_id' => $item->id,
|
||||
'qty' => $item->qty,
|
||||
'price' => $item->price,
|
||||
|
|
@ -677,21 +668,20 @@ class ShoppingUserController extends Controller
|
|||
'points' => $item->options->points,
|
||||
'slug' => $item->options->slug,
|
||||
])->save();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $model->delete();
|
||||
});
|
||||
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (! ShoppingOrderItem::where('shopping_order_id', $shopping_order->id)->where('row_id', $item->rowId)->count()) {
|
||||
if (!ShoppingOrderItem::where('shopping_order_id', $shopping_order->id)->where('row_id', $item->rowId)->count()){
|
||||
$price_net = Yard::instance('shopping')->rowPriceNet($item, 2, '.', '');
|
||||
$tax = $item->price - $price_net;
|
||||
ShoppingOrderItem::create([
|
||||
'shopping_order_id' => $shopping_order->id,
|
||||
'row_id' => $item->rowId,
|
||||
'row_id' => $item->rowId,
|
||||
'product_id' => $item->id,
|
||||
'qty' => $item->qty,
|
||||
'price' => $item->price,
|
||||
|
|
@ -701,27 +691,27 @@ class ShoppingUserController extends Controller
|
|||
'price_vk_net' => $shopping_order->getPriceVkNetBy($item->id),
|
||||
'discount' => $item->options->no_commission ? 0 : $shopping_order->getUserDiscount(),
|
||||
'points' => $item->options->points,
|
||||
'slug' => $item->options->slug,
|
||||
'slug' => $item->options->slug
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
$shopping_order->makeTaxSplit();
|
||||
|
||||
return $shopping_order;
|
||||
}
|
||||
|
||||
public function orderStatusSendMail(ShoppingOrder $shopping_order)
|
||||
{
|
||||
|
||||
public function orderStatusSendMail(ShoppingOrder $shopping_order){
|
||||
|
||||
$bcc = [];
|
||||
$user_mail = $shopping_order->shopping_user->member->email;
|
||||
if ($shopping_order->mode === 'dev') {
|
||||
if($shopping_order->mode === 'dev'){
|
||||
$bcc[] = config('app.checkout_test_mail');
|
||||
} else {
|
||||
}else{
|
||||
$bcc[] = config('app.checkout_mail');
|
||||
}
|
||||
|
||||
Mail::to($user_mail)->bcc($bcc)->locale($shopping_order->getLocale())->send(new MailCheckout($shopping_order->txaction, $shopping_order, null, false, $shopping_order->mode));
|
||||
Mail::to($user_mail)->bcc($bcc)->send(new MailCheckout($shopping_order->txaction, $shopping_order, null, false, $shopping_order->mode));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
0
app/Http/Controllers/AttributeController.php
Normal file → Executable file
0
app/Http/Controllers/AttributeController.php
Normal file → Executable file
0
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file → Executable file
0
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file → Executable file
36
app/Http/Controllers/Auth/LoginController.php
Normal file → Executable file
36
app/Http/Controllers/Auth/LoginController.php
Normal file → Executable file
|
|
@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
|
|||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
||||
class LoginController extends Controller
|
||||
|
|
@ -41,47 +40,12 @@ class LoginController extends Controller
|
|||
$this->middleware('guest')->except('logout');
|
||||
}
|
||||
|
||||
public function showLoginForm()
|
||||
{
|
||||
//login als Kunde, dann zum Login wechseln
|
||||
if(Auth::guard('customers')->check()){
|
||||
return redirect()->route('change_login');
|
||||
}
|
||||
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
public function showChangeLogin(){
|
||||
if(Auth::guard('customers')->check()){
|
||||
return view('auth.change');
|
||||
}
|
||||
if(Auth::guard('user')->check()){
|
||||
return redirect(route('home'));
|
||||
|
||||
}
|
||||
return redirect(route('login'));
|
||||
|
||||
}
|
||||
|
||||
public function confirmChangeLogin(Request $request)
|
||||
{
|
||||
//$url = Util::getMyMivitaShopUrl();
|
||||
$user_shop_domain = session('user_shop_domain');
|
||||
$locale = session('locale');
|
||||
Auth::guard('customers')->logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
session(['user_shop_domain' => $user_shop_domain]);
|
||||
session(['locale' => $locale]);
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
protected function authenticated(Request $request, $user)
|
||||
{
|
||||
$user->last_login = date('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
}
|
||||
|
||||
protected function handleUserWasAuthenticated(Request $request, $throttles)
|
||||
{
|
||||
|
||||
|
|
|
|||
0
app/Http/Controllers/Auth/RegisterController.php
Normal file → Executable file
0
app/Http/Controllers/Auth/RegisterController.php
Normal file → Executable file
0
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file → Executable file
0
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file → Executable file
|
|
@ -32,7 +32,7 @@ class BusinessCommissionController extends Controller
|
|||
|
||||
$this->setFilterVars();
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_months' => HTMLHelper::$months,
|
||||
'filter_years' => HTMLHelper::getYearRange(),
|
||||
'filter_members' => $filter_members,
|
||||
'filter_show' => $this->filter_show,
|
||||
|
|
@ -85,7 +85,7 @@ class BusinessCommissionController extends Controller
|
|||
|
||||
if(intval(Request::get('commissions_filter_show')) === 1){
|
||||
$query->where(function($q) {
|
||||
return $q->where('user_businesses.commission_pp_total', '>', 0)
|
||||
return $q->where('user_businesses.commission_team_total', '>', 0)
|
||||
->orWhere('user_businesses.commission_shop_sales', '>', 0);
|
||||
});
|
||||
}
|
||||
|
|
@ -108,20 +108,20 @@ class BusinessCommissionController extends Controller
|
|||
data-back=""
|
||||
data-modal="modal-xl"
|
||||
data-init_from="user"
|
||||
data-route="'.route('modal_load').'"><span class="fa fa-eye"></span></button>';
|
||||
data-route="'.route('modal_load').'"><span class="far fa-eye"></span></button>';
|
||||
})*/
|
||||
|
||||
|
||||
->addColumn('commission_total', function (UserBusiness $UserBusiness) {
|
||||
$commission_total = $UserBusiness->commission_pp_total + $UserBusiness->commission_shop_sales;
|
||||
$commission_total = $UserBusiness->commission_team_total + $UserBusiness->commission_shop_sales;
|
||||
return $commission_total > 0 ?
|
||||
'<span class="badge badge-outline-info">'.formatNumber($commission_total).' €</span>'
|
||||
: $commission_total.' €';
|
||||
})
|
||||
->addColumn('commission_pp_total', function (UserBusiness $UserBusiness) {
|
||||
return $UserBusiness->commission_pp_total > 0 ?
|
||||
'<span class="badge badge-outline-success">'.formatNumber($UserBusiness->commission_pp_total).' €</span>'
|
||||
: $UserBusiness->commission_pp_total.' €';
|
||||
->addColumn('commission_team_total', function (UserBusiness $UserBusiness) {
|
||||
return $UserBusiness->commission_team_total > 0 ?
|
||||
'<span class="badge badge-outline-success">'.formatNumber($UserBusiness->commission_team_total).' €</span>'
|
||||
: $UserBusiness->commission_team_total.' €';
|
||||
})
|
||||
->addColumn('commission_shop_sales', function (UserBusiness $UserBusiness) {
|
||||
return $UserBusiness->commission_shop_sales > 0 ?
|
||||
|
|
@ -157,13 +157,13 @@ class BusinessCommissionController extends Controller
|
|||
})*/
|
||||
|
||||
->orderColumn('id', 'id $1')
|
||||
->orderColumn('commission_pp_total', 'commission_pp_total $1')
|
||||
->orderColumn('commission_team_total', 'commission_team_total $1')
|
||||
->orderColumn('commission_shop_sales', 'commission_shop_sales $1')
|
||||
->orderColumn('email', 'users.email $1')
|
||||
->orderColumn('m_account', 'm_account $1')
|
||||
->orderColumn('first_name', 'first_name $1')
|
||||
->orderColumn('last_name', 'last_name $1')
|
||||
->rawColumns(['id', 'commission_total', 'commission_pp_total', 'commission_shop_sales', 'active_account'])
|
||||
->rawColumns(['id', 'commission_total', 'commission_team_total', 'commission_shop_sales', 'active_account'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ class BusinessController extends Controller
|
|||
private $filter_active = [1 => 'aktiv', 2 => 'nicht aktiv', 3 => 'alle'];
|
||||
private $month;
|
||||
private $year;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('admin');
|
||||
|
|
@ -25,28 +25,27 @@ class BusinessController extends Controller
|
|||
|
||||
public function show()
|
||||
{
|
||||
abort(403, 'This page is removed');
|
||||
$this->setFilterVars();
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_months' => HTMLHelper::$months,
|
||||
'filter_years' => HTMLHelper::getYearRange(),
|
||||
'filter_active' => $this->filter_active,
|
||||
|
||||
];
|
||||
return view('admin.business.show', $data);
|
||||
}
|
||||
|
||||
public function structure()
|
||||
{
|
||||
//abort(403, 'This page is removed');
|
||||
$this->setFilterVars();
|
||||
$this->month = session('business_user_filter_month');
|
||||
$this->year = session('business_user_filter_year');
|
||||
|
||||
$TreeCalcBot = new TreeCalcBot($this->month, $this->year, 'admin');
|
||||
$TreeCalcBot->initStructureAdmin();
|
||||
|
||||
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_months' => HTMLHelper::$months,
|
||||
'filter_years' => HTMLHelper::getYearRange(),
|
||||
'TreeCalcBot' => $TreeCalcBot,
|
||||
];
|
||||
|
|
@ -55,16 +54,16 @@ class BusinessController extends Controller
|
|||
|
||||
public function userDetail($user_id)
|
||||
{
|
||||
abort(403, 'This page is removed');
|
||||
$user = User::findOrFail($user_id);
|
||||
$this->setFilterVars();
|
||||
|
||||
$data = [];
|
||||
$data['month'] = session('business_user_filter_month');
|
||||
$data['year'] = session('business_user_filter_year');
|
||||
|
||||
$TreeCalcBot = new TreeCalcBot($data['month'], $data['year'], 'admin');
|
||||
$TreeCalcBot->initBusinesslUserDetail($user);
|
||||
if (!$TreeCalcBot->business_user) {
|
||||
if(!$TreeCalcBot->business_user){
|
||||
abort(403, 'no user found');
|
||||
}
|
||||
return view('admin.business.user_detail', compact('TreeCalcBot', 'user', 'data'));
|
||||
|
|
@ -85,52 +84,43 @@ class BusinessController extends Controller
|
|||
//return back();
|
||||
}
|
||||
|
||||
private function setFilterVars()
|
||||
{
|
||||
private function setFilterVars(){
|
||||
|
||||
if (!session('business_user_filter_month')) {
|
||||
if(!session('business_user_filter_month')){
|
||||
session(['business_user_filter_month' => intval(date('m'))]);
|
||||
}
|
||||
if (!session('business_user_filter_year')) {
|
||||
if(!session('business_user_filter_year')){
|
||||
session(['business_user_filter_year' => intval(date('Y'))]);
|
||||
}
|
||||
if (!session('business_user_filter_active')) {
|
||||
if(!session('business_user_filter_active')){
|
||||
session(['business_user_filter_active' => 1]);
|
||||
}
|
||||
if (!session('business_user_filter_depiction')) {
|
||||
session(['business_user_filter_depiction' => 'active']);
|
||||
}
|
||||
|
||||
if (Request::get('business_user_filter_depiction')) {
|
||||
session(['business_user_filter_depiction' => Request::get('business_user_filter_depiction')]);
|
||||
}
|
||||
if (Request::get('business_user_filter_name')) {
|
||||
if(Request::get('business_user_filter_name')){
|
||||
session(['business_user_filter_name' => Request::get('business_user_filter_name')]);
|
||||
} else {
|
||||
session(['business_user_filter_name' => '']);
|
||||
}
|
||||
if (Request::get('business_user_filter_active')) {
|
||||
if(Request::get('business_user_filter_active')){
|
||||
session(['business_user_filter_active' => Request::get('business_user_filter_active')]);
|
||||
}
|
||||
if (Request::get('business_user_filter_month')) {
|
||||
if(Request::get('business_user_filter_month')){
|
||||
session(['business_user_filter_month' => Request::get('business_user_filter_month')]);
|
||||
}
|
||||
if (Request::get('business_user_filter_year')) {
|
||||
if(Request::get('business_user_filter_year')){
|
||||
session(['business_user_filter_year' => Request::get('business_user_filter_year')]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function userDatatable()
|
||||
{
|
||||
{
|
||||
$this->month = Request::get('business_user_filter_month');
|
||||
$this->year = Request::get('business_user_filter_year');
|
||||
|
||||
//only the currently month get from Users -> older month from UserBusiness
|
||||
return $this->userCurrentlyDatatable();
|
||||
if (TreeCalcBot::isFromStored($this->month, $this->year)) {
|
||||
//return $this->userCurrentlyDatatable();
|
||||
if(TreeCalcBot::isFromStored($this->month, $this->year)){
|
||||
return $this->userStoredDatatable();
|
||||
} else {
|
||||
}else{
|
||||
return $this->userCurrentlyDatatable();
|
||||
}
|
||||
}
|
||||
|
|
@ -138,16 +128,16 @@ class BusinessController extends Controller
|
|||
private function initStoredSearch($archive = false, $request = true)
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
|
||||
$query = UserBusiness::select('user_businesses.*')->where('month', $this->month)->where('year', $this->year);
|
||||
if (Request::get('business_user_filter_active')) {
|
||||
if (Request::get('business_user_filter_active') == 1) {
|
||||
if(Request::get('business_user_filter_active')){
|
||||
if(Request::get('business_user_filter_active') == 1){
|
||||
$query->where('user_businesses.active_account', 1);
|
||||
}
|
||||
if (Request::get('business_user_filter_active') == 2) {
|
||||
if(Request::get('business_user_filter_active') == 2){
|
||||
$query->where('user_businesses.active_account', 0);
|
||||
}
|
||||
if (Request::get('business_user_filter_active') == 3) {
|
||||
if(Request::get('business_user_filter_active') == 3){
|
||||
//both -> payment_account only not null
|
||||
}
|
||||
}
|
||||
|
|
@ -155,18 +145,18 @@ class BusinessController extends Controller
|
|||
}
|
||||
|
||||
private function userStoredDatatable()
|
||||
{
|
||||
{
|
||||
$query = $this->initStoredSearch();
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (UserBusiness $userBusiness) {
|
||||
return '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
|
||||
data-id="' . $userBusiness->user_id . '"
|
||||
data-id="'.$userBusiness->user_id.'"
|
||||
data-action="business-user-detail"
|
||||
data-back=""
|
||||
data-modal="modal-xl"
|
||||
data-init_from="admin"
|
||||
data-route="' . route('modal_load') . '"><span class="fa fa-calculator"></span></button>' .
|
||||
(config('app.debug') === true ? '<a href="' . route('admin_business_user_detail', [$userBusiness->user_id]) . '" class="btn icon-btn btn-xs btn-primary"><span class="fa fa-calculator"></span></a>' : '');
|
||||
data-route="'.route('modal_load').'"><span class="far fa-calculator"></span></button>';
|
||||
//.'<a href="' . route('admin_business_user_detail', [$userBusiness->user_id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="far fa-calculator"></span></a>';
|
||||
})
|
||||
->addColumn('m_account', function (UserBusiness $userBusiness) {
|
||||
return $userBusiness->m_account;
|
||||
|
|
@ -175,19 +165,19 @@ class BusinessController extends Controller
|
|||
return $userBusiness->user_level_name;
|
||||
})
|
||||
->addColumn('is_qual_kp', function (UserBusiness $userBusiness) {
|
||||
if ($userBusiness->m_level_id) {
|
||||
if($userBusiness->m_level_id){
|
||||
$isQualKP = ($userBusiness->sales_volume_points_sum >= $userBusiness->qual_kp) ? true : false;
|
||||
return '<span class="badge ' . ($isQualKP ? 'badge-outline-success' : 'badge-outline-danger') . '"> KU ' . $userBusiness->qual_kp . '</span>';
|
||||
return '<span class="badge '.($isQualKP ? 'badge-outline-success' : 'badge-outline-danger').'"> KD '.$userBusiness->qual_kp.'</span>';
|
||||
}
|
||||
return '-';
|
||||
})
|
||||
->addColumn('sales_volume_KP_points', function (UserBusiness $userBusiness) {
|
||||
return '<div class="no-line-break">' . $userBusiness->sales_volume_points_sum . '</div>' .
|
||||
'<span class="small no-line-break">E: ' . $userBusiness->sales_volume_KP_points . ' | S: ' . $userBusiness->sales_volume_points_shop . '</span>';
|
||||
->addColumn('sales_volume_points', function (UserBusiness $userBusiness) {
|
||||
return '<div class="no-line-break">'.$userBusiness->sales_volume_points_sum.'</div>'.
|
||||
'<span class="small no-line-break">E: '.$userBusiness->sales_volume_points.' | S: '.$userBusiness->sales_volume_points_shop.'</span>';
|
||||
})
|
||||
->addColumn('sales_volume_total', function (UserBusiness $userBusiness) {
|
||||
return '<div class="no-line-break">' . formatNumber($userBusiness->sales_volume_total_sum) . ' €</div>' .
|
||||
'<span class="small no-line-break">E: ' . formatNumber($userBusiness->sales_volume_total) . ' | S: ' . formatNumber($userBusiness->sales_volume_total_shop) . '</span>';
|
||||
return '<div class="no-line-break">'.formatNumber($userBusiness->sales_volume_total_sum).' €</div>'.
|
||||
'<span class="small no-line-break">E: '.formatNumber($userBusiness->sales_volume_total).' | S: '.formatNumber($userBusiness->sales_volume_total_shop).'</span>';
|
||||
})
|
||||
->addColumn('email', function (UserBusiness $userBusiness) {
|
||||
return $userBusiness->email;
|
||||
|
|
@ -199,23 +189,23 @@ class BusinessController extends Controller
|
|||
return $userBusiness->last_name;
|
||||
})
|
||||
->addColumn('sponsor', function (UserBusiness $userBusiness) {
|
||||
if ($userBusiness->sponsor) {
|
||||
if($userBusiness->sponsor){
|
||||
$sponsor = "";
|
||||
if ($userBusiness->sponsor->is_sponsor) {
|
||||
$sponsor .= $userBusiness->sponsor->first_name . " " . $userBusiness->sponsor->last_name;
|
||||
$sponsor .= " " . '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
|
||||
data-id="' . $userBusiness->sponsor->user_id . '"
|
||||
if($userBusiness->sponsor->is_sponsor){
|
||||
$sponsor .= $userBusiness->sponsor->first_name." ".$userBusiness->sponsor->last_name;
|
||||
$sponsor .= " ".'<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
|
||||
data-id="'.$userBusiness->sponsor->user_id.'"
|
||||
data-action="business-user-detail"
|
||||
data-back=""
|
||||
data-modal="modal-xl"
|
||||
data-init_from="admin"
|
||||
data-route="' . route('modal_load') . '"><span class="fa fa-calculator"></span></button><br>';
|
||||
|
||||
$sponsor .= '<span class="small no-line-break">' . $userBusiness->sponsor->email;
|
||||
$sponsor .= ' | ' . $userBusiness->sponsor->m_account;
|
||||
$sponsor .= '</span>';
|
||||
}
|
||||
data-route="'.route('modal_load').'"><span class="far fa-calculator"></span></button><br>';
|
||||
|
||||
$sponsor .= '<span class="small no-line-break">'.$userBusiness->sponsor->email;
|
||||
$sponsor .= ' | '.$userBusiness->sponsor->m_account;
|
||||
$sponsor .= '</span>';
|
||||
}
|
||||
|
||||
return $sponsor;
|
||||
}
|
||||
return '-';
|
||||
|
|
@ -228,25 +218,25 @@ class BusinessController extends Controller
|
|||
return $userBusiness->active_date ? formatDate($userBusiness->active_date) : "-";
|
||||
})
|
||||
|
||||
|
||||
->filterColumn('m_account', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("m_account LIKE ?", '%' . $keyword . '%');
|
||||
|
||||
->filterColumn('m_account', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("m_account LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("first_name LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("first_name LIKE ?", '%' . $keyword . '%');
|
||||
->filterColumn('last_name', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("last_name LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('last_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("last_name LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('email', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("email LIKE ?", '%' . $keyword . '%');
|
||||
->filterColumn('email', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("email LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -256,32 +246,32 @@ class BusinessController extends Controller
|
|||
->orderColumn('first_name', 'first_name $1')
|
||||
->orderColumn('last_name', 'last_name $1')
|
||||
->orderColumn('active_account', 'payment_account $1')
|
||||
->rawColumns(['id', 'is_qual_kp', 'sales_volume_KP_points', 'sales_volume_total', 'sponsor', 'active_account'])
|
||||
->rawColumns(['id', 'is_qual_kp', 'sales_volume_points', 'sales_volume_total', 'sponsor', 'active_account'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
|
||||
private function initCurrentlySearch($archive = false, $request = true)
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
|
||||
$query = User::join('user_accounts', 'account_id', '=', 'user_accounts.id')
|
||||
->select('users.*', 'user_accounts.m_account', 'user_accounts.first_name', 'user_accounts.last_name')
|
||||
->where('users.deleted_at', '=', null)
|
||||
->where('users.id', '!=', 1)
|
||||
->where('users.admin', "<", 4)
|
||||
->where('users.m_level', "!=", null)
|
||||
->where('users.payment_account', "!=", null);
|
||||
->select('users.*', 'user_accounts.m_account', 'user_accounts.first_name', 'user_accounts.last_name')
|
||||
->where('users.deleted_at', '=', null)
|
||||
->where('users.id', '!=', 1)
|
||||
->where('users.admin', "<", 4)
|
||||
->where('users.m_level', "!=", null)
|
||||
->where('users.payment_account', "!=", null);
|
||||
|
||||
// $query = User::with('account')->select('users.*')
|
||||
|
||||
if (Request::get('business_user_filter_active')) {
|
||||
if (Request::get('business_user_filter_active') == 1) {
|
||||
// $query = User::with('account')->select('users.*')
|
||||
|
||||
if(Request::get('business_user_filter_active')){
|
||||
if(Request::get('business_user_filter_active') == 1){
|
||||
$query->where('users.payment_account', ">=", now());
|
||||
}
|
||||
if (Request::get('business_user_filter_active') == 2) {
|
||||
if(Request::get('business_user_filter_active') == 2){
|
||||
$query->where('users.payment_account', "<", now());
|
||||
}
|
||||
if (Request::get('business_user_filter_active') == 3) {
|
||||
if(Request::get('business_user_filter_active') == 3){
|
||||
//both -> payment_account only not null
|
||||
}
|
||||
}
|
||||
|
|
@ -289,41 +279,41 @@ class BusinessController extends Controller
|
|||
}
|
||||
|
||||
private function userCurrentlyDatatable()
|
||||
{
|
||||
{
|
||||
$query = $this->initCurrentlySearch();
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (User $user) {
|
||||
return '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
|
||||
data-id="' . $user->id . '"
|
||||
data-id="'.$user->id.'"
|
||||
data-action="business-user-detail"
|
||||
data-back=""
|
||||
data-modal="modal-xl"
|
||||
data-init_from="admin"
|
||||
data-route="' . route('modal_load') . '"><span class="fa fa-calculator"></span></button>' .
|
||||
(config('app.debug') === true ? '<a href="' . route('admin_business_user_detail', [$user->id]) . '" class="btn icon-btn btn-xs btn-primary"><span class="fa fa-calculator"></span></a>' : '');
|
||||
data-route="'.route('modal_load').'"><span class="far fa-calculator"></span></button>';
|
||||
//.'<a href="' . route('admin_business_user_detail', [$user->id]) . '" class="btn icon-btn btn-sm btn-info"><span class="far fa-calculator"></span></a>';
|
||||
})
|
||||
->addColumn('m_account', function (User $user) {
|
||||
return $user->account ? $user->account->m_account : '';
|
||||
})
|
||||
->addColumn('user_level', function (User $user) {
|
||||
return $user->user_level ? $user->user_level->getLang('name') : '';
|
||||
return $user->user_level ? $user->user_level->name : '';
|
||||
})
|
||||
->addColumn('is_qual_kp', function (User $user) {
|
||||
if ($user->user_level) {
|
||||
if($user->user_level){
|
||||
$qual_kp = $user->user_level->qual_kp;
|
||||
$sales_volume_points_sum = $user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_points_KP_sum');
|
||||
$sales_volume_points_sum = $user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_points_sum');
|
||||
$isQualKP = ($sales_volume_points_sum >= $qual_kp) ? true : false;
|
||||
return '<span class="badge ' . ($isQualKP ? 'badge-outline-success' : 'badge-outline-warning-dark') . '"> KU ' . $qual_kp . '</span>';
|
||||
return '<span class="badge '.($isQualKP ? 'badge-outline-success' : 'badge-outline-danger').'"> KD '.$qual_kp.'</span>';
|
||||
}
|
||||
return '-';
|
||||
})
|
||||
->addColumn('sales_volume_KP_points', function (User $user) {
|
||||
return '<div class="no-line-break">' . $user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_points_KP_sum') . '</div>' .
|
||||
'<span class="small no-line-break">E: ' . $user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_KP_points') . ' | S: ' . $user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_points_shop') . '</span>';
|
||||
})
|
||||
->addColumn('sales_volume_points', function (User $user) {
|
||||
return '<div class="no-line-break">'.$user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_points_sum').'</div>'.
|
||||
'<span class="small no-line-break">E: '.$user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_points').' | S: '.$user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_points_shop').'</span>';
|
||||
})
|
||||
->addColumn('sales_volume_total', function (User $user) {
|
||||
return '<div class="no-line-break">' . formatNumber($user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_total_sum')) . ' €</div>' .
|
||||
'<span class="small no-line-break">E: ' . formatNumber($user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_total')) . ' | S: ' . formatNumber($user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_total_shop')) . '</span>';
|
||||
return '<div class="no-line-break">'.formatNumber($user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_total_sum')).' €</div>'.
|
||||
'<span class="small no-line-break">E: '.formatNumber($user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_total')).' | S: '.formatNumber($user->getUserSalesVolumeBy($this->month, $this->year, 'sales_volume_total_shop')).'</span>';
|
||||
})
|
||||
->addColumn('email', function (User $user) {
|
||||
return $user->email;
|
||||
|
|
@ -335,24 +325,24 @@ class BusinessController extends Controller
|
|||
return $user->account ? $user->account->last_name : '';
|
||||
})
|
||||
->addColumn('sponsor', function (User $user) {
|
||||
if ($user->user_sponsor) {
|
||||
if($user->user_sponsor){
|
||||
$sponsor = "";
|
||||
if ($user->user_sponsor->account) {
|
||||
$sponsor .= $user->user_sponsor->account->first_name . " " . $user->user_sponsor->account->last_name;
|
||||
$sponsor .= " " . '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
|
||||
data-id="' . $user->user_sponsor->id . '"
|
||||
if($user->user_sponsor->account){
|
||||
$sponsor .= $user->user_sponsor->account->first_name." ".$user->user_sponsor->account->last_name;
|
||||
$sponsor .= " ".'<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
|
||||
data-id="'.$user->user_sponsor->id.'"
|
||||
data-action="business-user-detail"
|
||||
data-back=""
|
||||
data-modal="modal-xl"
|
||||
data-init_from="admin"
|
||||
data-route="' . route('modal_load') . '"><span class="fa fa-calculator"></span></button><br>';
|
||||
}
|
||||
$sponsor .= '<span class="small no-line-break">' . $user->user_sponsor->email;
|
||||
if ($user->user_sponsor->account) {
|
||||
$sponsor .= ' | ' . $user->user_sponsor->account->m_account;
|
||||
}
|
||||
$sponsor .= '</span>';
|
||||
|
||||
data-route="'.route('modal_load').'"><span class="far fa-calculator"></span></button><br>';
|
||||
}
|
||||
$sponsor .= '<span class="small no-line-break">'.$user->user_sponsor->email;
|
||||
if($user->user_sponsor->account){
|
||||
$sponsor .= ' | '.$user->user_sponsor->account->m_account;
|
||||
}
|
||||
$sponsor .= '</span>';
|
||||
|
||||
return $sponsor;
|
||||
}
|
||||
return '-';
|
||||
|
|
@ -364,25 +354,25 @@ class BusinessController extends Controller
|
|||
->addColumn('payment_account_date', function (User $user) {
|
||||
return $user->payment_account ? $user->getPaymentAccountDateFormat(false) : "-";
|
||||
})
|
||||
|
||||
->filterColumn('m_account', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("m_account LIKE ?", '%' . $keyword . '%');
|
||||
|
||||
->filterColumn('m_account', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("m_account LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("first_name LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("first_name LIKE ?", '%' . $keyword . '%');
|
||||
->filterColumn('last_name', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("last_name LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('last_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("last_name LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('email', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("email LIKE ?", '%' . $keyword . '%');
|
||||
->filterColumn('email', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("email LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->orderColumn('id', 'm_account $1')
|
||||
|
|
@ -391,7 +381,7 @@ class BusinessController extends Controller
|
|||
->orderColumn('email', 'email $1')
|
||||
->orderColumn('last_name', 'last_name $1')
|
||||
->orderColumn('active_account', 'payment_account $1')
|
||||
->rawColumns(['id', 'is_qual_kp', 'sales_volume_KP_points', 'sales_volume_total', 'sponsor', 'active_account'])
|
||||
->rawColumns(['id', 'is_qual_kp', 'sales_volume_points', 'sales_volume_total', 'sponsor', 'active_account'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,575 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\UserBusiness;
|
||||
use App\Models\UserBusinessStructure;
|
||||
use App\Services\BusinessPlan\BusinessUserRepository;
|
||||
use App\Services\BusinessPlan\TreeCalcBotOptimized;
|
||||
use App\Services\BusinessPlan\TreeHelperOptimized;
|
||||
use App\Services\BusinessPlan\TreeHtmlRenderer;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\Services\NextLevelBadgeHelper;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* Optimierte Version des BusinessController
|
||||
*
|
||||
* Verbesserungen:
|
||||
* - Nutzt TreeCalcBotOptimized für bessere Performance
|
||||
* - Optimierte Datenbankabfragen durch Repository Pattern
|
||||
* - Memory-effiziente Verarbeitung großer Datenmengen
|
||||
* - Robuste Fehlerbehandlung mit Logging
|
||||
* - Performance-Monitoring für Debugging
|
||||
*/
|
||||
class BusinessControllerOptimized extends Controller
|
||||
{
|
||||
private $filter_active = [1 => 'aktiv', 2 => 'nicht aktiv', 3 => 'alle'];
|
||||
private $filter_next_level = [
|
||||
0 => 'Alle Status',
|
||||
1 => 'Qualifiziert (grün)',
|
||||
2 => 'In Arbeit (gelb)',
|
||||
3 => 'Kein Level (rot)'
|
||||
];
|
||||
private $month;
|
||||
private $year;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt die Business-Übersicht (identisch zur Original-Version)
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_years' => HTMLHelper::getYearRange(),
|
||||
'filter_active' => $this->filter_active,
|
||||
'filter_levels' => $this->getFilterLevels(),
|
||||
'filter_next_level' => $this->filter_next_level,
|
||||
'optimized' => true, // Flag für View um zu zeigen, dass optimierte Version läuft
|
||||
];
|
||||
|
||||
return view('admin.business_optimized.show', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt die Business-Struktur mit optimierter TreeCalcBot-Version
|
||||
*/
|
||||
public function structure()
|
||||
{
|
||||
$startTime = microtime(true);
|
||||
$startMemory = memory_get_usage();
|
||||
|
||||
try {
|
||||
$this->setFilterVars();
|
||||
$this->month = session('business_user_filter_month');
|
||||
$this->year = session('business_user_filter_year');
|
||||
|
||||
Log::info("BusinessControllerOptimized: Building structure for {$this->month}/{$this->year}");
|
||||
|
||||
// Verwende optimierte TreeCalcBot-Version
|
||||
$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'admin');
|
||||
|
||||
// Prüfe ob Live-Berechnung für Struktur erzwungen wird
|
||||
$forceLiveCalculation = Request::get('force_live_calculation', false) ||
|
||||
Request::get('force_live_structure', false) ||
|
||||
Request::get('live', false);
|
||||
|
||||
if ($forceLiveCalculation) {
|
||||
Log::info("BusinessControllerOptimized: Force live calculation requested");
|
||||
$TreeCalcBot->initStructureAdmin(true, $forceLiveCalculation); // check=true, forceLiveCalculation=true
|
||||
} else {
|
||||
Log::info("BusinessControllerOptimized: Force live calculation not requested");
|
||||
$TreeCalcBot->initStructureAdmin(); // Standard: verwende gespeicherte wenn verfügbar
|
||||
}
|
||||
|
||||
$endTime = microtime(true);
|
||||
$endMemory = memory_get_usage();
|
||||
|
||||
$executionTime = round(($endTime - $startTime) * 1000, 2);
|
||||
$memoryUsed = $this->formatBytes($endMemory - $startMemory);
|
||||
|
||||
$calculationType = $forceLiveCalculation ? " (LIVE)" : " (CACHE)";
|
||||
Log::info("BusinessControllerOptimized: Structure built in {$executionTime}ms, Memory: {$memoryUsed}{$calculationType}");
|
||||
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_years' => HTMLHelper::getYearRange(),
|
||||
'TreeCalcBot' => $TreeCalcBot,
|
||||
'performance' => [
|
||||
'execution_time' => $executionTime,
|
||||
'memory_used' => $memoryUsed,
|
||||
'user_count' => $TreeCalcBot->getTotalUserCount(),
|
||||
'parentless_count' => $TreeCalcBot->isParentless() ? count($TreeCalcBot->__get('parentless')) : 0,
|
||||
'calculation_type' => $forceLiveCalculation ? 'Live' : 'Cache'
|
||||
],
|
||||
'optimized' => true,
|
||||
'forceLiveCalculation' => $forceLiveCalculation,
|
||||
];
|
||||
|
||||
return view('admin.business_optimized.structure', $data);
|
||||
} catch (\Exception $e) {
|
||||
Log::error("BusinessControllerOptimized: Error in structure: " . $e->getMessage());
|
||||
|
||||
return view('admin.business_optimized.error', [
|
||||
'error' => $e->getMessage(),
|
||||
'month' => $this->month,
|
||||
'year' => $this->year
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt User-Details mit optimierter Performance
|
||||
*/
|
||||
public function userDetail($user_id)
|
||||
{
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$user = User::with(['account', 'user_level', 'user_sponsor.account'])->findOrFail($user_id);
|
||||
$this->setFilterVars();
|
||||
|
||||
$data = [];
|
||||
$data['month'] = session('business_user_filter_month');
|
||||
$data['year'] = session('business_user_filter_year');
|
||||
|
||||
Log::info("BusinessControllerOptimized: Building user detail for user {$user_id}");
|
||||
|
||||
$TreeCalcBot = new TreeCalcBotOptimized($data['month'], $data['year'], 'admin');
|
||||
|
||||
// Prüfe ob Live-Berechnung über URL-Parameter erzwungen wird
|
||||
$forceLiveCalculation = Request::get('force_live_calculation', false) ||
|
||||
Request::get('force_live', false) ||
|
||||
Request::get('live', false);
|
||||
|
||||
if ($forceLiveCalculation) {
|
||||
Log::info("BusinessControllerOptimized: Force live calculation requested for user {$user_id}");
|
||||
}
|
||||
|
||||
$TreeCalcBot->initBusinesslUserDetail($user, $forceLiveCalculation);
|
||||
|
||||
if (!$TreeCalcBot->__get('business_user')) {
|
||||
Log::warning("BusinessControllerOptimized: No business user found for {$user_id}");
|
||||
abort(403, 'No business user found');
|
||||
}
|
||||
|
||||
$endTime = microtime(true);
|
||||
$executionTime = round(($endTime - $startTime) * 1000, 2);
|
||||
|
||||
$data['performance'] = [
|
||||
'execution_time' => $executionTime,
|
||||
'user_id' => $user_id,
|
||||
'calculation_type' => $forceLiveCalculation ? 'Live' : 'Cache'
|
||||
];
|
||||
|
||||
$data['forceLiveCalculation'] = $forceLiveCalculation;
|
||||
|
||||
$calculationType = $forceLiveCalculation ? " (LIVE)" : " (CACHE)";
|
||||
Log::info("BusinessControllerOptimized: User detail built in {$executionTime}ms{$calculationType}");
|
||||
|
||||
return view('admin.business_optimized.user_detail', compact('TreeCalcBot', 'user', 'data'));
|
||||
} catch (\Exception $e) {
|
||||
Log::error("BusinessControllerOptimized: Error in userDetail for {$user_id}: " . $e->getMessage());
|
||||
|
||||
return view('admin.business_optimized.error', [
|
||||
'error' => $e->getMessage(),
|
||||
'user_id' => $user_id
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store-Funktion (identisch zur Original-Version)
|
||||
*/
|
||||
public function userStore($user_id)
|
||||
{
|
||||
dd('function on: App\Console\Commands\BusinessStore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimierte DataTable für Users mit besserer Performance
|
||||
*/
|
||||
public function userDatatable(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$this->month = Request::get('business_user_filter_month');
|
||||
$this->year = Request::get('business_user_filter_year');
|
||||
|
||||
Log::info("BusinessControllerOptimized: Building datatable for {$this->month}/{$this->year}");
|
||||
|
||||
// Prüfe ob optimierte Repository-Daten verfügbar sind
|
||||
if (TreeCalcBotOptimized::isFromStored($this->month, $this->year)) {
|
||||
return $this->userStoredDatatableOptimized();
|
||||
} else {
|
||||
return $this->userCurrentlyDatatableOptimized();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("BusinessControllerOptimized: Error in userDatatable: " . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'error' => 'Datatable could not be loaded: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimierte Stored-Datatable mit besserer Query-Performance
|
||||
*/
|
||||
private function userStoredDatatableOptimized(): JsonResponse
|
||||
{
|
||||
$query = $this->initStoredSearchOptimized();
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (UserBusiness $userBusiness) {
|
||||
return TreeHelperOptimized::generateActionButtons($userBusiness->user_id);
|
||||
})
|
||||
->addColumn('m_account', function (UserBusiness $userBusiness) {
|
||||
return e($userBusiness->m_account);
|
||||
})
|
||||
->addColumn('user_level', function (UserBusiness $userBusiness) {
|
||||
return e($userBusiness->user_level_name);
|
||||
})
|
||||
->addColumn('is_qual_kp', function (UserBusiness $userBusiness) {
|
||||
return TreeHelperOptimized::generateQualKPBadge($userBusiness);
|
||||
})
|
||||
->addColumn('sales_volume_KP_points', function (UserBusiness $userBusiness) {
|
||||
return TreeHelperOptimized::generateSalesVolumeDisplay($userBusiness, 'points');
|
||||
})
|
||||
->addColumn('sales_volume_total', function (UserBusiness $userBusiness) {
|
||||
return TreeHelperOptimized::generateSalesVolumeDisplay($userBusiness, 'total');
|
||||
})
|
||||
->addColumn('email', function (UserBusiness $userBusiness) {
|
||||
return e($userBusiness->email);
|
||||
})
|
||||
->addColumn('first_name', function (UserBusiness $userBusiness) {
|
||||
return e($userBusiness->first_name);
|
||||
})
|
||||
->addColumn('last_name', function (UserBusiness $userBusiness) {
|
||||
return e($userBusiness->last_name);
|
||||
})
|
||||
->addColumn('sponsor', function (UserBusiness $userBusiness) {
|
||||
return TreeHelperOptimized::generateSponsorDisplay($userBusiness);
|
||||
})
|
||||
->addColumn('active_account', function (UserBusiness $userBusiness) {
|
||||
return get_active_badge($userBusiness->active_account);
|
||||
})
|
||||
->addColumn('next_level_qualified', function (UserBusiness $userBusiness) {
|
||||
return NextLevelBadgeHelper::generateBadgeFromUserBusiness($userBusiness);
|
||||
})
|
||||
->addColumn('payment_account_date', function (UserBusiness $userBusiness) {
|
||||
return $userBusiness->active_date ? formatDate($userBusiness->active_date) : "-";
|
||||
})
|
||||
->filterColumn('m_account', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("user_businesses.m_account LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("user_businesses.first_name LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('last_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("user_businesses.last_name LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('email', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("user_businesses.email LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->orderColumn('id', 'id $1')
|
||||
->orderColumn('m_account', 'm_account $1')
|
||||
->orderColumn('email', 'email $1')
|
||||
->orderColumn('first_name', 'first_name $1')
|
||||
->orderColumn('last_name', 'last_name $1')
|
||||
->orderColumn('active_account', 'payment_account $1')
|
||||
->rawColumns(['id', 'is_qual_kp', 'sales_volume_KP_points', 'sales_volume_total', 'sponsor', 'active_account', 'next_level_qualified'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimierte Currently-Datatable mit Repository Pattern
|
||||
*/
|
||||
private function userCurrentlyDatatableOptimized(): JsonResponse
|
||||
{
|
||||
$repository = new BusinessUserRepository($this->month, $this->year);
|
||||
|
||||
// Nutze Repository für optimierte Abfragen
|
||||
$query = $this->initCurrentlySearchOptimized();
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (User $user) {
|
||||
return TreeHelperOptimized::generateActionButtons($user->id);
|
||||
})
|
||||
->addColumn('m_account', function (User $user) {
|
||||
return $user->account ? e($user->account->m_account) : '';
|
||||
})
|
||||
->addColumn('user_level', function (User $user) {
|
||||
return $user->user_level ? e($user->user_level->getLang('name')) : '';
|
||||
})
|
||||
->addColumn('is_qual_kp', function (User $user) {
|
||||
return TreeHelperOptimized::generateQualKPBadgeForUser($user, $this->month, $this->year);
|
||||
})
|
||||
->addColumn('sales_volume_KP_points', function (User $user) {
|
||||
return TreeHelperOptimized::generateSalesVolumeDisplayForUser($user, 'points', $this->month, $this->year);
|
||||
})
|
||||
->addColumn('sales_volume_total', function (User $user) {
|
||||
return TreeHelperOptimized::generateSalesVolumeDisplayForUser($user, 'total', $this->month, $this->year);
|
||||
})
|
||||
->addColumn('email', function (User $user) {
|
||||
return e($user->email);
|
||||
})
|
||||
->addColumn('first_name', function (User $user) {
|
||||
return $user->account ? e($user->account->first_name) : '';
|
||||
})
|
||||
->addColumn('last_name', function (User $user) {
|
||||
return $user->account ? e($user->account->last_name) : '';
|
||||
})
|
||||
->addColumn('sponsor', function (User $user) {
|
||||
return TreeHelperOptimized::generateSponsorDisplayForUser($user);
|
||||
})
|
||||
->addColumn('active_account', function (User $user) {
|
||||
return get_active_badge($user->isActiveAccount());
|
||||
})
|
||||
->addColumn('next_level_qualified', function (User $user) {
|
||||
// Für Live-DataTable: Verwende bereits berechnete Daten wenn verfügbar
|
||||
$userBusiness = UserBusiness::where('user_id', $user->id)
|
||||
->where('month', $this->month)
|
||||
->where('year', $this->year)
|
||||
->first();
|
||||
|
||||
if ($userBusiness) {
|
||||
return NextLevelBadgeHelper::generateBadgeFromUserBusiness($userBusiness);
|
||||
}
|
||||
|
||||
return NextLevelBadgeHelper::renderNoDataBadge();
|
||||
})
|
||||
->addColumn('payment_account_date', function (User $user) {
|
||||
return $user->payment_account ? $user->getPaymentAccountDateFormat(false) : "-";
|
||||
})
|
||||
->filterColumn('m_account', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("user_accounts.m_account LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("user_accounts.first_name LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('last_name', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("user_accounts.last_name LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->filterColumn('email', function ($query, $keyword) {
|
||||
if ($keyword != "") {
|
||||
$query->whereRaw("users.email LIKE ?", '%' . $keyword . '%');
|
||||
}
|
||||
})
|
||||
->orderColumn('id', 'users.id $1')
|
||||
->orderColumn('m_account', 'user_accounts.m_account $1')
|
||||
->orderColumn('first_name', 'user_accounts.first_name $1')
|
||||
->orderColumn('last_name', 'user_accounts.last_name $1')
|
||||
->orderColumn('email', 'users.email $1')
|
||||
->orderColumn('active_account', 'users.payment_account $1')
|
||||
->rawColumns(['id', 'is_qual_kp', 'sales_volume_KP_points', 'sales_volume_total', 'sponsor', 'active_account', 'next_level_qualified'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
// ===== PRIVATE HELPER METHODS =====
|
||||
|
||||
/**
|
||||
* Optimierte Stored Search Query
|
||||
*/
|
||||
private function initStoredSearchOptimized()
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
$query = UserBusiness::select('user_businesses.*')
|
||||
->where('month', $this->month)
|
||||
->where('year', $this->year);
|
||||
|
||||
$activeFilter = Request::get('business_user_filter_active') ?: session('business_user_filter_active');
|
||||
if ($activeFilter == 1) {
|
||||
$query->where('user_businesses.active_account', 1);
|
||||
} elseif ($activeFilter == 2) {
|
||||
$query->where('user_businesses.active_account', 0);
|
||||
}
|
||||
// activeFilter == 3 bedeutet alle (keine weitere Einschränkung)
|
||||
|
||||
$levelFilter = Request::get('business_user_filter_level') ?: session('business_user_filter_level');
|
||||
if ($levelFilter && $levelFilter != 0) {
|
||||
$query->where('user_businesses.m_level_id', $levelFilter);
|
||||
}
|
||||
|
||||
$nextLevelFilter = Request::get('business_user_filter_next_level') ?: session('business_user_filter_next_level');
|
||||
if ($nextLevelFilter && $nextLevelFilter != 0) {
|
||||
switch ($nextLevelFilter) {
|
||||
case 1: // Qualifiziert (grün) - hat next_qual_user_level
|
||||
$query->whereNotNull('user_businesses.next_qual_user_level')
|
||||
->where('user_businesses.next_qual_user_level', '!=', '[]');
|
||||
break;
|
||||
case 2: // In Arbeit (gelb) - hat next_can_user_level aber kein next_qual_user_level
|
||||
$query->where(function ($q) {
|
||||
$q->whereNull('user_businesses.next_qual_user_level')
|
||||
->orWhere('user_businesses.next_qual_user_level', '=', '[]');
|
||||
})
|
||||
->whereNotNull('user_businesses.next_can_user_level')
|
||||
->where('user_businesses.next_can_user_level', '!=', '[]');
|
||||
break;
|
||||
case 3: // Kein Level (rot) - hat weder next_qual noch next_can
|
||||
$query->where(function ($q) {
|
||||
$q->where(function ($q1) {
|
||||
$q1->whereNull('user_businesses.next_qual_user_level')
|
||||
->orWhere('user_businesses.next_qual_user_level', '=', '[]');
|
||||
})
|
||||
->where(function ($q2) {
|
||||
$q2->whereNull('user_businesses.next_can_user_level')
|
||||
->orWhere('user_businesses.next_can_user_level', '=', '[]');
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimierte Currently Search Query mit besseren Joins
|
||||
*/
|
||||
private function initCurrentlySearchOptimized()
|
||||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
$query = User::with(['account', 'user_level', 'user_sponsor.account'])
|
||||
->select('users.*', 'user_accounts.m_account', 'user_accounts.first_name', 'user_accounts.last_name')
|
||||
->leftJoin('user_accounts', 'users.id', '=', 'user_accounts.id')
|
||||
->where('users.deleted_at', '=', null)
|
||||
->where('users.id', '!=', 1)
|
||||
->where('users.admin', '<', 4)
|
||||
->where('users.m_level', '!=', null)
|
||||
->where('users.payment_account', '!=', null);
|
||||
|
||||
$activeFilter = Request::get('business_user_filter_active') ?: session('business_user_filter_active');
|
||||
if ($activeFilter == 1) {
|
||||
$query->where('users.payment_account', '>=', now());
|
||||
} elseif ($activeFilter == 2) {
|
||||
$query->where('users.payment_account', '<', now());
|
||||
}
|
||||
// activeFilter == 3 bedeutet alle (keine weitere Einschränkung)
|
||||
|
||||
$levelFilter = Request::get('business_user_filter_level') ?: session('business_user_filter_level');
|
||||
if ($levelFilter && $levelFilter != 0) {
|
||||
$query->where('users.m_level', $levelFilter);
|
||||
}
|
||||
|
||||
// Next-Level-Filter wird bei Live-Berechnungen ignoriert (Performance-Gründe)
|
||||
// Dieser Filter funktioniert nur mit gespeicherten Daten
|
||||
$nextLevelFilter = Request::get('business_user_filter_next_level') ?: session('business_user_filter_next_level');
|
||||
if ($nextLevelFilter && $nextLevelFilter != 0) {
|
||||
Log::info("BusinessControllerOptimized: Next-Level-Filter bei Live-Berechnung ignoriert (Performance-Gründe)");
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter-Variablen setzen (identisch zur Original-Version)
|
||||
*/
|
||||
private function setFilterVars()
|
||||
{
|
||||
if (!session('business_user_filter_month')) {
|
||||
session(['business_user_filter_month' => intval(date('m'))]);
|
||||
}
|
||||
if (!session('business_user_filter_year')) {
|
||||
session(['business_user_filter_year' => intval(date('Y'))]);
|
||||
}
|
||||
if (!session('business_user_filter_active')) {
|
||||
session(['business_user_filter_active' => 1]);
|
||||
}
|
||||
if (!session('business_user_filter_level')) {
|
||||
session(['business_user_filter_level' => 0]);
|
||||
}
|
||||
if (!session('business_user_filter_next_level')) {
|
||||
session(['business_user_filter_next_level' => 0]);
|
||||
}
|
||||
if (!session('business_user_filter_depiction')) {
|
||||
session(['business_user_filter_depiction' => 'active']);
|
||||
}
|
||||
|
||||
if (Request::get('business_user_filter_depiction')) {
|
||||
session(['business_user_filter_depiction' => Request::get('business_user_filter_depiction')]);
|
||||
}
|
||||
if (Request::get('business_user_filter_name')) {
|
||||
session(['business_user_filter_name' => Request::get('business_user_filter_name')]);
|
||||
} else {
|
||||
session(['business_user_filter_name' => '']);
|
||||
}
|
||||
if (Request::get('business_user_filter_active')) {
|
||||
session(['business_user_filter_active' => Request::get('business_user_filter_active')]);
|
||||
}
|
||||
if (Request::get('business_user_filter_level')) {
|
||||
session(['business_user_filter_level' => Request::get('business_user_filter_level')]);
|
||||
} else {
|
||||
session(['business_user_filter_level' => 0]);
|
||||
}
|
||||
if (Request::get('business_user_filter_next_level')) {
|
||||
session(['business_user_filter_next_level' => Request::get('business_user_filter_next_level')]);
|
||||
} else {
|
||||
session(['business_user_filter_next_level' => 0]);
|
||||
}
|
||||
if (Request::get('business_user_filter_month')) {
|
||||
session(['business_user_filter_month' => Request::get('business_user_filter_month')]);
|
||||
}
|
||||
if (Request::get('business_user_filter_year')) {
|
||||
session(['business_user_filter_year' => Request::get('business_user_filter_year')]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert Bytes in lesbare Einheiten
|
||||
*/
|
||||
private function formatBytes(int $bytes, int $precision = 2): string
|
||||
{
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt verfügbare User Level für Filter
|
||||
*/
|
||||
private function getFilterLevels(): array
|
||||
{
|
||||
$levels = [0 => 'Alle Level'];
|
||||
|
||||
$userLevels = \App\Models\UserLevel::orderBy('pos')->get(['id', 'name']);
|
||||
foreach ($userLevels as $level) {
|
||||
$levels[$level->id] = $level->name;
|
||||
}
|
||||
|
||||
return $levels;
|
||||
}
|
||||
|
||||
// Performance-optimierte Badge-Generierung wurde in NextLevelBadgeHelper ausgelagert
|
||||
// Alte performance-lastige Methoden wurden entfernt um die Datatable-Performance zu verbessern
|
||||
}
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
use Carbon;
|
||||
use Request;
|
||||
use App\Services\Payment;
|
||||
use App\Models\UserInvoice;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\Models\UserSalesVolume;
|
||||
use App\Services\BusinessPlan\SalesPointsVolume;
|
||||
use App\Services\HTMLHelper;
|
||||
use Request;
|
||||
|
||||
class BusinessPointsController extends Controller
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('admin');
|
||||
|
|
@ -16,100 +20,67 @@ class BusinessPointsController extends Controller
|
|||
|
||||
public function index()
|
||||
{
|
||||
|
||||
|
||||
$filter_members = UserSalesVolume::join('users', 'user_id', '=', 'users.id')
|
||||
->groupBy('user_id')->join('user_accounts', 'account_id', '=', 'user_accounts.id')
|
||||
->select('users.id', 'users.email', 'user_accounts.first_name', 'user_accounts.last_name')->get();
|
||||
->groupBy('user_id')->join('user_accounts', 'account_id', '=', 'user_accounts.id')
|
||||
->select('users.id', 'users.email', 'user_accounts.first_name', 'user_accounts.last_name')->get();
|
||||
|
||||
|
||||
$this->setFilterVars();
|
||||
$data = [
|
||||
'filter_months' => HTMLHelper::getTransMonths(),
|
||||
'filter_months' => HTMLHelper::$months,
|
||||
'filter_years' => HTMLHelper::getYearRange(),
|
||||
'filter_members' => $filter_members,
|
||||
'filter_status_types' => UserSalesVolume::getTransStatusType(),
|
||||
|
||||
];
|
||||
|
||||
return view('admin.business.points', $data);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
public function store(){
|
||||
$data = Request::all();
|
||||
if (! isset($data['action'])) {
|
||||
if(!isset($data['action'])){
|
||||
return back();
|
||||
}
|
||||
if (! isset($data['change_member_key']) || $data['change_member_key'] !== config('mivita.edit_data_pass')) {
|
||||
if(!isset($data['change_member_key']) || $data['change_member_key'] !== config('mivita.edit_data_pass')){
|
||||
\Session()->flash('alert-error', 'Das Passwort ist falsch.');
|
||||
|
||||
return back();
|
||||
return back();
|
||||
}
|
||||
if (! isset($data['is_checked_action'])) {
|
||||
if(!isset($data['is_checked_action'])){
|
||||
\Session()->flash('alert-error', 'Änderung nicht bestätigt');
|
||||
|
||||
return back();
|
||||
return back();
|
||||
}
|
||||
|
||||
if ($data['action'] === 'add_user_sales_volume') {
|
||||
if($data['action'] === 'add_user_sales_volume'){
|
||||
SalesPointsVolume::addSalesPointsVolume($data);
|
||||
return back(); }
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
if ($data['action'] === 'edit_user_sales_volume') {
|
||||
if($data['action'] === 'edit_user_sales_volume'){
|
||||
SalesPointsVolume::editSalesPointsVolume($data);
|
||||
return back();
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
dd($data);
|
||||
|
||||
return redirect(route('admin_business_points'));
|
||||
}
|
||||
|
||||
public function recalculate()
|
||||
{
|
||||
$user_id = Request::get('points_filter_member_id');
|
||||
$month = Request::get('points_filter_month');
|
||||
$year = Request::get('points_filter_year');
|
||||
|
||||
if (! $user_id) {
|
||||
\Session()->flash('alert-error', 'Kein Berater ausgewählt.');
|
||||
private function setFilterVars(){
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
if (! $month || ! $year) {
|
||||
\Session()->flash('alert-error', 'Monat und Jahr müssen angegeben sein.');
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
try {
|
||||
SalesPointsVolume::reCalculateSalesPointsVolume($user_id, $month, $year);
|
||||
\Session()->flash('alert-success', 'Punkte für den ausgewählten Berater im Monat '.str_pad($month, 2, '0', STR_PAD_LEFT).'/'.$year.' wurden erfolgreich neu berechnet.');
|
||||
} catch (\Exception $e) {
|
||||
\Session()->flash('alert-error', 'Fehler bei der Neuberechnung: '.$e->getMessage());
|
||||
}
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
private function setFilterVars()
|
||||
{
|
||||
|
||||
if (! session('points_filter_month')) {
|
||||
if(!session('points_filter_month')){
|
||||
session(['points_filter_month' => intval(date('m'))]);
|
||||
}
|
||||
if (! session('points_filter_year')) {
|
||||
if(!session('points_filter_year')){
|
||||
session(['points_filter_year' => intval(date('Y'))]);
|
||||
}
|
||||
|
||||
|
||||
session(['points_filter_member_id' => Request::get('points_filter_member_id')]);
|
||||
session(['points_filter_status_type_id' => Request::get('points_filter_status_type_id')]);
|
||||
|
||||
if (Request::get('points_filter_month')) {
|
||||
|
||||
if(Request::get('points_filter_month')){
|
||||
session(['points_filter_month' => Request::get('points_filter_month')]);
|
||||
}
|
||||
if (Request::get('points_filter_year')) {
|
||||
if(Request::get('points_filter_year')){
|
||||
session(['points_filter_year' => Request::get('points_filter_year')]);
|
||||
}
|
||||
}
|
||||
|
|
@ -118,69 +89,21 @@ class BusinessPointsController extends Controller
|
|||
{
|
||||
$this->setFilterVars();
|
||||
|
||||
// $query = UserSalesVolume::with('user', 'user.account')->with('shopping_order')->select('user_sales_volumes.*')
|
||||
//$query = UserSalesVolume::with('user', 'user.account')->with('shopping_order')->select('user_sales_volumes.*')
|
||||
$query = UserSalesVolume::join('users', 'user_id', '=', 'users.id')->join('user_accounts', 'account_id', '=', 'user_accounts.id')
|
||||
->select('user_sales_volumes.*', 'users.email', 'user_accounts.m_account', 'user_accounts.first_name', 'user_accounts.last_name')
|
||||
->where('user_sales_volumes.month', '=', Request::get('points_filter_month'))
|
||||
->where('user_sales_volumes.year', '=', Request::get('points_filter_year'));
|
||||
->select('user_sales_volumes.*', 'users.email', 'user_accounts.m_account', 'user_accounts.first_name', 'user_accounts.last_name')
|
||||
->where('user_sales_volumes.month', '=', Request::get('points_filter_month'))
|
||||
->where('user_sales_volumes.year', '=', Request::get('points_filter_year'));
|
||||
|
||||
if (Request::get('points_filter_member_id')) {
|
||||
if(Request::get('points_filter_member_id')){
|
||||
$query->where('user_sales_volumes.user_id', '=', Request::get('points_filter_member_id'));
|
||||
}
|
||||
if (Request::get('points_filter_status_type_id')) {
|
||||
$query->where('user_sales_volumes.status', '=', Request::get('points_filter_status_type_id'));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
$user_id = Request::get('points_filter_member_id');
|
||||
$month = Request::get('points_filter_month');
|
||||
$year = Request::get('points_filter_year');
|
||||
|
||||
if (! $user_id || ! $month || ! $year) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
// Hole den letzten Eintrag für den User im Monat, da dort die akkumulierten Summen stehen
|
||||
$lastEntry = UserSalesVolume::where('user_id', $user_id)
|
||||
->where('month', $month)
|
||||
->where('year', $year)
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
if (! $lastEntry) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'month_KP_points' => $lastEntry->month_KP_points ?? 0,
|
||||
'month_TP_points' => $lastEntry->month_TP_points ?? 0,
|
||||
'month_shop_points' => $lastEntry->month_shop_points ?? 0,
|
||||
'month_total_net' => $lastEntry->month_total_net ?? 0,
|
||||
'month_shop_total_net' => $lastEntry->month_shop_total_net ?? 0,
|
||||
'total_KP_points' => ($lastEntry->month_KP_points ?? 0) + ($lastEntry->month_shop_points ?? 0),
|
||||
'total_TP_points' => ($lastEntry->month_TP_points ?? 0) + ($lastEntry->month_shop_points ?? 0),
|
||||
'total_net' => ($lastEntry->month_total_net ?? 0) + ($lastEntry->month_shop_total_net ?? 0),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function datatable()
|
||||
{
|
||||
public function datatable(){
|
||||
|
||||
$query = $this->initSearch();
|
||||
|
||||
return \DataTables::eloquent($query)
|
||||
->addColumn('id', function (UserSalesVolume $UserSalesVolume) {
|
||||
return '<button type="button" class="btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
|
||||
|
|
@ -189,74 +112,53 @@ class BusinessPointsController extends Controller
|
|||
data-back=""
|
||||
data-modal="modal-xl"
|
||||
data-init_from="user"
|
||||
data-route="'.route('modal_load').'"><span class="fa fa-eye"></span></button>';
|
||||
data-route="'.route('modal_load').'"><span class="far fa-eye"></span></button>';
|
||||
})
|
||||
->addColumn('order', function (UserSalesVolume $UserSalesVolume) {
|
||||
if ($UserSalesVolume->shopping_order) {
|
||||
// Status 1 = Beraterbestellung
|
||||
if ($UserSalesVolume->status === 1) {
|
||||
return '<a href="'.route('admin_sales_users_detail', [$UserSalesVolume->shopping_order->id]).'" class="btn btn-xs btn-primary">'.$UserSalesVolume->shopping_order->id.'</a>';
|
||||
if($UserSalesVolume->shopping_order){
|
||||
if($UserSalesVolume->status === 1){
|
||||
return '<a href="' . route('admin_sales_users_detail', [$UserSalesVolume->shopping_order->id]) . '" class="btn btn-xs btn-primary">'.$UserSalesVolume->shopping_order->id.'</a>';
|
||||
}
|
||||
// Status 2/3 = Shop-Bestellung
|
||||
if ($UserSalesVolume->status === 2 || $UserSalesVolume->status === 3) {
|
||||
return '<a href="'.route('admin_sales_customers_detail', [$UserSalesVolume->shopping_order->id]).'" class="btn btn-xs btn-secondary">'.$UserSalesVolume->shopping_order->id.'</a>';
|
||||
}
|
||||
// Status 6 = Storno - Link zur ursprünglichen Bestellung mit Storno-Icon
|
||||
if ($UserSalesVolume->status === 6) {
|
||||
// Prüfen ob Berater- oder Shop-Bestellung anhand des payment_for Feldes
|
||||
$route = ($UserSalesVolume->shopping_order->payment_for === 6 || $UserSalesVolume->shopping_order->payment_for === 7)
|
||||
? route('admin_sales_customers_detail', [$UserSalesVolume->shopping_order->id])
|
||||
: route('admin_sales_users_detail', [$UserSalesVolume->shopping_order->id]);
|
||||
|
||||
return '<a href="'.$route.'" class="btn btn-xs btn-danger" title="Storno-Eintrag"><i class="fa fa-undo"></i> '.$UserSalesVolume->shopping_order->id.'</a>';
|
||||
if($UserSalesVolume->status === 2 || $UserSalesVolume->status === 3){
|
||||
return '<a href="' . route('admin_sales_customers_detail', [$UserSalesVolume->shopping_order->id]) . '" class="btn btn-xs btn-secondary">'.$UserSalesVolume->shopping_order->id.'</a>';
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
->addColumn('points', function (UserSalesVolume $UserSalesVolume) {
|
||||
return formatNumber($UserSalesVolume->points);
|
||||
})
|
||||
->addColumn('total_net', function (UserSalesVolume $UserSalesVolume) {
|
||||
return formatNumber($UserSalesVolume->total_net).' €';
|
||||
})
|
||||
->addColumn('status_turnover', function (UserSalesVolume $UserSalesVolume) {
|
||||
return '<span class="badge badge-pill badge-'.$UserSalesVolume->getStatusTurnoverColor().'">'.$UserSalesVolume->getStatusTurnoverType().'</span>';
|
||||
})
|
||||
->addColumn('status', function (UserSalesVolume $UserSalesVolume) {
|
||||
return '<span class="badge badge-pill badge-'.$UserSalesVolume->getStatusColor().'">'.$UserSalesVolume->getStatusType().'</span>';
|
||||
})
|
||||
->addColumn('status_points', function (UserSalesVolume $UserSalesVolume) {
|
||||
return '<span class="badge badge-pill badge-'.$UserSalesVolume->getStatusPointsColor().'">'.$UserSalesVolume->getStatusPointsType().'</span>';
|
||||
})
|
||||
->addColumn('message', function (UserSalesVolume $UserSalesVolume) {
|
||||
return '<span class="no-line-break">'.$UserSalesVolume->message.'</span>';
|
||||
})
|
||||
->addColumn('info', function (UserSalesVolume $UserSalesVolume) {
|
||||
return '<span class="no-line-break">'.$UserSalesVolume->info.'</span>';
|
||||
})
|
||||
|
||||
->filterColumn('m_account', function ($query, $keyword) {
|
||||
if ($keyword != '') {
|
||||
$query->whereRaw('m_account LIKE ?', '%'.$keyword.'%');
|
||||
|
||||
->filterColumn('m_account', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("m_account LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("first_name LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('first_name', function ($query, $keyword) {
|
||||
if ($keyword != '') {
|
||||
$query->whereRaw('first_name LIKE ?', '%'.$keyword.'%');
|
||||
->filterColumn('last_name', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("last_name LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('last_name', function ($query, $keyword) {
|
||||
if ($keyword != '') {
|
||||
$query->whereRaw('last_name LIKE ?', '%'.$keyword.'%');
|
||||
->filterColumn('email', function($query, $keyword) {
|
||||
if($keyword != ""){
|
||||
$query->whereRaw("email LIKE ?", '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
->filterColumn('email', function ($query, $keyword) {
|
||||
if ($keyword != '') {
|
||||
$query->whereRaw('email LIKE ?', '%'.$keyword.'%');
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
->orderColumn('id', 'id $1')
|
||||
->orderColumn('order', 'order $1')
|
||||
->orderColumn('status', 'status $1')
|
||||
|
|
@ -267,7 +169,7 @@ class BusinessPointsController extends Controller
|
|||
->orderColumn('m_account', 'm_account $1')
|
||||
->orderColumn('first_name', 'first_name $1')
|
||||
->orderColumn('last_name', 'last_name $1')
|
||||
->rawColumns(['id', 'order', 'status_turnover', 'status', 'status_points', 'message', 'info', 'total_net'])
|
||||
->rawColumns(['id', 'order', 'status', 'message', 'info', 'total_net'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
95
app/Http/Controllers/CategoryController.php
Normal file → Executable file
95
app/Http/Controllers/CategoryController.php
Normal file → Executable file
|
|
@ -28,10 +28,10 @@ class CategoryController extends Controller
|
|||
|
||||
public function edit($id)
|
||||
{
|
||||
if ($id == "new") {
|
||||
if($id == "new"){
|
||||
$model = new Category();
|
||||
$model->active = true;
|
||||
} else {
|
||||
}else{
|
||||
$model = Category::findOrFail($id);
|
||||
}
|
||||
$data = [
|
||||
|
|
@ -46,9 +46,9 @@ class CategoryController extends Controller
|
|||
{
|
||||
|
||||
$data = Request::all();
|
||||
if ($data['action'] === 'save-product_category') {
|
||||
if($data['action'] === 'save-product_category'){
|
||||
|
||||
if ($data['id'] === 'new') {
|
||||
if($data['id'] === 'new'){
|
||||
$ProductCategory = ProductCategory::create([
|
||||
'pos' => $data['pos'],
|
||||
'product_id' => $data['product_id'],
|
||||
|
|
@ -56,9 +56,9 @@ class CategoryController extends Controller
|
|||
]);
|
||||
\Session()->flash('alert-save', '1');
|
||||
return redirect(route('admin_product_category_edit', [$ProductCategory->category_id]));
|
||||
} else {
|
||||
}else{
|
||||
$ProductCategory = ProductCategory::findOrFail($data['id']);
|
||||
if ($ProductCategory->category_id != $data['category_id']) {
|
||||
if($ProductCategory->category_id != $data['category_id']){
|
||||
abort(404);
|
||||
}
|
||||
$ProductCategory->pos = $data['pos'];
|
||||
|
|
@ -67,64 +67,64 @@ class CategoryController extends Controller
|
|||
\Session()->flash('alert-save', '1');
|
||||
return redirect(route('admin_product_category_edit', [$ProductCategory->category_id]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($data['action'] === 'save-form') {
|
||||
if($data['action'] === 'save-form'){
|
||||
$data['active'] = isset($data['active']) ? true : false;
|
||||
$data['parent_id'] = isset($data['parent_id']) ? $data['parent_id'] : null;
|
||||
if ($data['id'] == "new") {
|
||||
if($data['id'] == "new"){
|
||||
$model = Category::create($data);
|
||||
} else {
|
||||
}else{
|
||||
$model = Category::find($data['id']);
|
||||
$model->fill($data)->save();
|
||||
}
|
||||
|
||||
|
||||
$trans = [];
|
||||
if (!empty($data['trans_name'])) {
|
||||
|
||||
foreach ($data['trans_name'] as $lang => $value) {
|
||||
if ($value && $value != null) {
|
||||
if(!empty($data['trans_name'])){
|
||||
|
||||
foreach ($data['trans_name'] as $lang => $value){
|
||||
if($value && $value != null){
|
||||
$trans[$lang] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$model->trans_name = $trans;
|
||||
$model->save();
|
||||
|
||||
|
||||
$trans = [];
|
||||
if (!empty($data['trans_headline'])) {
|
||||
foreach ($data['trans_headline'] as $lang => $value) {
|
||||
if ($value && $value != null) {
|
||||
if(!empty($data['trans_headline'])){
|
||||
foreach ($data['trans_headline'] as $lang => $value){
|
||||
if($value && $value != null){
|
||||
$trans[$lang] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$model->trans_headline = $trans;
|
||||
$model->save();
|
||||
|
||||
|
||||
\Session()->flash('alert-save', '1');
|
||||
return redirect(route('admin_product_categories'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function delete($do, $id)
|
||||
{
|
||||
public function delete($do, $id){
|
||||
|
||||
if ($do === 'product_category') {
|
||||
if($do === 'product_category'){
|
||||
$model = ProductCategory::findOrFail($id);
|
||||
$category = $model->category;
|
||||
$model->delete();
|
||||
\Session()->flash('alert-success', 'Eintrag gelöscht');
|
||||
return redirect(route('admin_product_category_edit', [$category->id]));
|
||||
}
|
||||
if ($do === 'category') {
|
||||
if (ProductCategory::where('category_id', $id)->count()) {
|
||||
if($do === 'category'){
|
||||
if(ProductCategory::where('category_id', $id)->count()){
|
||||
\Session()->flash('alert-error', 'Eintrag hat noch Produkte, erst löschen');
|
||||
return redirect(route('admin_product_categories'));
|
||||
}
|
||||
if (Category::where('parent_id', $id)->count()) {
|
||||
\Session()->flash('alert-error', 'Eintrag wird als Haupt-Kategorie verwendet');
|
||||
if(Category::where('parent_id', $id)->count()){
|
||||
\Session()->flash('alert-error', 'Eintrag wird als Haup-Kategorie verwendet');
|
||||
return redirect(route('admin_product_categories'));
|
||||
}
|
||||
$model = Category::findOrFail($id);
|
||||
|
|
@ -132,12 +132,12 @@ class CategoryController extends Controller
|
|||
\Session()->flash('alert-success', 'Eintrag gelöscht');
|
||||
return redirect(route('admin_product_categories'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Upload FILE -----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
public function imageUpload()
|
||||
{
|
||||
public function imageUpload(){
|
||||
|
||||
$category_id = Request::get('category_id');
|
||||
$category = Category::findOrFail($category_id);
|
||||
|
|
@ -145,11 +145,12 @@ class CategoryController extends Controller
|
|||
try {
|
||||
$image = \App\Services\Slim::getImages('images')[0];
|
||||
|
||||
if (isset($image['output']['data'])) {
|
||||
if ( isset($image['output']['data']) )
|
||||
{
|
||||
|
||||
// Base64 of the image
|
||||
$data = $image['output']['data'];
|
||||
$file_ex = array('image/jpeg' => 'jpg', 'image/png' => 'png');
|
||||
$file_ex = array( 'image/jpeg' => 'jpg', 'image/png' => 'png');
|
||||
|
||||
if (!isset($file_ex[$image['output']['type']])) {
|
||||
\Session()->flash('alert-danger', 'File is not jpg or png!');
|
||||
|
|
@ -165,10 +166,10 @@ class CategoryController extends Controller
|
|||
$image_name = "";
|
||||
do {
|
||||
$image_name = uniqid('', false) . '_' . $name;
|
||||
} while (\Storage::disk('public')->exists($path . $image_name));
|
||||
} while (\Storage::disk('public')->exists($path.$image_name));
|
||||
|
||||
$data = \Storage::disk('public')->put(
|
||||
$path . $image_name,
|
||||
$path.$image_name,
|
||||
$data
|
||||
);
|
||||
|
||||
|
|
@ -183,40 +184,42 @@ class CategoryController extends Controller
|
|||
$category->headline_image_id = $iq_image->id;
|
||||
$category->save();
|
||||
|
||||
\Session()->flash('alert-success', __('msg.file_uploaded'));
|
||||
\Session()->flash('alert-success', "Datei hochgeladen");
|
||||
return redirect(route('admin_product_category_edit', [$category->id]));
|
||||
}
|
||||
\Session()->flash('alert-danger', __('msg.file_empty'));
|
||||
\Session()->flash('alert-danger', "Datei leer");
|
||||
return redirect(route('admin_product_category_edit', [$category->id]));
|
||||
} catch (Exception $e) {
|
||||
\Session()->flash('alert-danger', "Error: " . $e);
|
||||
|
||||
}
|
||||
catch (Exception $e) {
|
||||
\Session()->flash('alert-danger', "Fehler".$e);
|
||||
return redirect(route('admin_product_category_edit', [$category->id]));
|
||||
}
|
||||
}
|
||||
|
||||
public function imageDelete($image_id, $category_id)
|
||||
{
|
||||
public function imageDelete($image_id, $category_id){
|
||||
|
||||
$category = Category::findOrFail($category_id);
|
||||
$iq_image = IqImage::findOrFail($image_id);
|
||||
|
||||
if ($iq_image->id == $category->iq_image->id) {
|
||||
$file = 'images/iq_images/' . $iq_image->filename;
|
||||
if($iq_image->id == $category->iq_image->id){
|
||||
$file = 'images/iq_images/'.$iq_image->filename;
|
||||
\Storage::disk('public')->delete($file);
|
||||
$category->headline_image_id = NULL;
|
||||
$category->save();
|
||||
$iq_image->delete();
|
||||
|
||||
|
||||
\Session()->flash('alert-success', __('msg.file_deleted'));
|
||||
\Session()->flash('alert-success', "Datei gelöscht");
|
||||
return redirect(route('admin_product_category_edit', [$category->id]));
|
||||
|
||||
}
|
||||
\Session()->flash('alert-danger', __('msg.file_not_found'));
|
||||
\Session()->flash('alert-danger', "Datei nicht gefunden");
|
||||
return redirect(route('admin_product_category_edit', [$category->id]));
|
||||
|
||||
}
|
||||
|
||||
public function imageAttribute($image_id, $attr, $val = false)
|
||||
{
|
||||
public function imageAttribute($image_id, $attr, $val = false){
|
||||
|
||||
$iq_image = IqImage::findOrFail($image_id);
|
||||
|
||||
|
|
@ -225,5 +228,7 @@ class CategoryController extends Controller
|
|||
|
||||
\Session()->flash('alert-success', "Wert gespeichert");
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
0
app/Http/Controllers/Controller.php
Normal file → Executable file
0
app/Http/Controllers/Controller.php
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue