commit 08-2025

This commit is contained in:
Kevin Adametz 2025-08-12 18:01:59 +02:00
parent 824fa43e96
commit 3b2253147b
393 changed files with 43842 additions and 18418 deletions

14
.gitignore vendored
View file

@ -25,6 +25,18 @@ Icon
Network Trash Folder
Temporary Items
.apdisk
.idea/
.claude/
.cursor/
.cursorrules/
.cursorrules.md/
.cursorrules.md.txt/
.cursorrules.md.txt.txt/
.cursorrules.md.txt.txt.txt/
_static/
_work/
_storage/
/vendor
/node_modules
@ -32,4 +44,4 @@ Temporary Items
/storage/framework
/storage/logs
/public/vendor
/storage/app
/storage/app

View file

@ -0,0 +1,69 @@
<?php
namespace App\Domain;
use App\Models\UserShop;
use Illuminate\Support\Arr;
/**
* DomainContext ist ein unveränderliches Datenobjekt, das den Kontext
* der aktuellen Domain für eine einzelne Anfrage enthält.
*
* Es wird vom DomainServiceProvider erstellt und im Service-Container
* registriert, damit andere Teile der Anwendung darauf zugreifen können.
*/
final class DomainContext
{
/**
* @param string $type Der Typ der Domain (z.B. 'main', 'crm', 'user-shop').
* @param string $host Der vollständige Hostname (z.B. 'my.mivita.care').
* @param string|null $subdomain Die extrahierte Subdomain (z.B. 'my').
* @param UserShop|null $userShop Das zugehörige UserShop-Objekt, falls vorhanden.
*/
public function __construct(
public readonly string $type,
public readonly string $host,
public readonly ?string $subdomain,
public readonly ?UserShop $userShop
) {
}
/**
* Erstellt eine neue Instanz aus einem Array von Domain-Informationen.
*/
public static function fromArray(array $domainInfo, ?UserShop $userShop = null): self
{
return new self(
Arr::get($domainInfo, 'type', 'unknown'),
Arr::get($domainInfo, 'host', ''),
Arr::get($domainInfo, 'subdomain'),
$userShop
);
}
/**
* Prüft, ob es sich um eine bekannte Domain handelt.
*/
public function isUnknown(): bool
{
return $this->type === 'unknown';
}
/**
* Prüft, ob es sich um eine User-Shop-Domain handelt.
*/
public function isUserShop(): bool
{
return $this->type === 'user-shop';
}
/**
* Gibt den Slug des User-Shops zurück.
*/
public function getUserShopSlug(): ?string
{
return $this->userShop?->slug;
}
}

View file

@ -61,6 +61,35 @@ 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 {
// Versuche domain-spezifische Login-Route
$context = app(\App\Domain\DomainContext::class);
$loginRoute = match($context->type) {
'portal' => 'portal.login.form',
'crm' => 'login', // CRM hat eine eigene login route
default => 'login'
};
return redirect()->guest(route($loginRoute));
} 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 {
@ -69,14 +98,25 @@ class Handler extends ExceptionHandler
$css = $handler->getStylesheet();
$content = $handler->getBody($e);
//Mail::to(config('app.exception_mail'))->send(new MailContact($contact));
\Mail::send('emails.exception', compact('css','content'), function ($message) {
$message
->to(config('app.exception_mail'))
->subject('mivita Exception: ' . \Request::fullUrl())
;
});
// 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) {
Log::error($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
);
}
}
}

View file

@ -0,0 +1,342 @@
<?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");
}*/
}

View file

@ -65,11 +65,11 @@ class AdminUserController extends Controller
$data = Request::all();
$user = User::findOrFail($data['id']);
if(isset($data['user-delete'])){
/* if(isset($data['user-delete'])){
if(isset($data['realy_delete_user'])){
return redirect(route('admin_user_delete', [$user->id]));
}
}
}*/
if(isset($data['save-admin'])){
$user->admin = $data['admin'];
SysLog::action('save-admin', 'admin_user', 3)
@ -187,12 +187,19 @@ class AdminUserController extends Controller
return redirect('/admin/users');
}
public function deleteUser($user_id)
public function deleteUser()
{
$user = User::findOrFail($user_id);
$this->userRepo->deleteUser($user);
\Session()->flash('alert-success', __('msg.contact_delete'));
$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'));
}
return redirect('/admin/users');
}
@ -208,12 +215,28 @@ class AdminUserController extends Controller
public function getUsers()
{
$query = User::with('account')->select('users.*')->where('users.deleted_at', '=', null)->where('users.admin', "<", 5);
$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 : '';
})
@ -288,11 +311,12 @@ class AdminUserController extends Controller
})
->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', 'admin', 'confirmed', 'active', 'account', 'shop', 'my_payment_methods', 'test_mode', 'action_login', 'action_delete'])
->rawColumns(['id', 'email', 'admin', 'confirmed', 'active', 'account', 'shop', 'my_payment_methods', 'test_mode', 'action_login', 'action_delete'])
->make(true);
}
}

View file

@ -2,96 +2,60 @@
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Product as ModelsProduct;
use Vitalybaev\GoogleMerchant\Feed;
use Vitalybaev\GoogleMerchant\Product;
use Vitalybaev\GoogleMerchant\Product\Shipping;
use Vitalybaev\GoogleMerchant\Product\Availability\Availability;
use Illuminate\Http\Response;
use Wearepixel\LaravelGoogleShoppingFeed\LaravelGoogleShoppingFeed;
use App\Services\Util;
class GoogleMerchantController extends Controller
{
public function __construct() {}
public function __construct()
/**
* Generate Google Merchant feed
*
* @return Response
*/
public function 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");
$feed = LaravelGoogleShoppingFeed::init(
'mivita shop',
'Bio Aloe Vera & Naturkosmetik',
'https://mivita.shop'
);
// Put products to the feed ($products - some data from database for example)
// Put products to the feed
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);
$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)
]);
}
// Here we get complete XML of the feed, that we could write to file or send directly
$feedXml = $feed->build();
print ($feedXml);
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
}
// http://api.mivita.test/google/merchant/feed
}

View file

@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
@ -40,12 +41,47 @@ 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)
{

View file

@ -0,0 +1,577 @@
<?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 {
$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_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', 'users.id $1')
->orderColumn('m_account', 'user_accounts.m_account $1')
->orderColumn('first_name', 'first_name $1')
->orderColumn('last_name', '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
}

View file

@ -129,6 +129,21 @@ class HomeController extends Controller
return abort(404);
}
*/
public function zahlungsarten(){
return view('web.templates.zahlungsarten', [
'user_shop' => Util::getUserShop(),
'isMivitaShop' => Util::isMivitaShop(),
'yard_instance' => 'webshop',
]);
}
public function versandkosten(){
return view('web.templates.versandkosten', [
'user_shop' => Util::getUserShop(),
'isMivitaShop' => Util::isMivitaShop(),
'yard_instance' => 'webshop',
]);
}
public function legalDataProtected()
{
@ -136,6 +151,7 @@ class HomeController extends Controller
'modal' => false,
'user_shop' => Util::getUserShop(),
'isMivitaShop' => Util::isMivitaShop(),
'yard_instance' => 'webshop',
];
return view('legal.data_protected', $data);
}
@ -145,15 +161,20 @@ class HomeController extends Controller
$data = [
'modal' => false,
'user_shop' => Util::getUserShop(),
'yard_instance' => 'webshop',
'yard_instance' => 'webshop',
];
return view('legal.agb', $data);
}
public function legalImprint()
{
$data = [
'modal' => false,
'user_shop' => Util::getUserShop(),
'yard_instance' => 'webshop',
];
return view('legal.imprint', $data);
}

View file

@ -84,7 +84,7 @@ class LeadController extends Controller
$user->account->shipping_country_id = 1;
$user->id = "new";
}else{
$user = User::findOrFail($id);
$user = User::withTrashed()->findOrFail($id);
if(!$user->account){
$user->account = new UserAccount();
}
@ -122,7 +122,7 @@ class LeadController extends Controller
$user->account->shipping_country_id = 1;
$user->id = "new";
}else{
$user = User::findOrFail($id);
$user = User::withTrashed()->findOrFail($id);
if(!$user->account){
$user->account = new UserAccount();
}

View file

@ -16,6 +16,7 @@ use App\Models\HomepartyUser;
use App\Models\ShoppingOrder;
use App\Models\UserSalesVolume;
use App\Services\BusinessPlan\TreeCalcBot;
use App\Services\BusinessPlan\TreeCalcBotOptimized;
class ModalController extends Controller
{
@ -122,6 +123,8 @@ class ModalController extends Controller
$data['month'] = session('team_user_filter_month');
$data['year'] = session('team_user_filter_year');
}
$data['live'] = $data['live'] ?? false;
$data['optimized'] = $data['optimized'] ?? false;
$TreeCalcBot = $this->getForBusinessUserDetail($user, $data);
$route = "";
$ret = view("admin.modal.business_user_detail", compact('TreeCalcBot', 'user', 'data'))->render();
@ -176,9 +179,13 @@ class ModalController extends Controller
//$auth_user = \Auth::user();
//if($auth_user->isAdmin() || $auth_user->id === $user->id){
if($data['optimized']){
$TreeCalcBot = new TreeCalcBotOptimized($data['month'], $data['year'], $data['init_from'], $data['live']);
}else{
$TreeCalcBot = new TreeCalcBot($data['month'], $data['year'], $data['init_from']);
$TreeCalcBot->initBusinesslUserDetail($user);
//TODO is not Admin, read is user in Parent tree ...
}
$TreeCalcBot->initBusinesslUserDetail($user, $data['live']);
//TODO is not Admin, read is user in Parent tree ...
if(!$TreeCalcBot->business_user){
abort(403, 'no user found');
}

View file

@ -36,7 +36,7 @@ class PaymentMethodController extends Controller
'short' => $data['short'],
'pos' => $data['pos'],
'show_on' => isset($data['show_on']) ? $data['show_on'] : null,
'is_abo' => isset($data['is_abo']) ? $data['is_abo'] : null,
'is_abo' => isset($data['is_abo']) ? $data['is_abo'] : false,
'default' => isset($data['default']) ? true : false,
'active' => isset($data['active']) ? true : false,
]);

View file

@ -0,0 +1,311 @@
<?php
namespace App\Http\Controllers\Portal;
use Auth;
use Yard;
use Request;
use Validator;
use App\Services\Shop;
use App\Services\Util;
use App\Models\Product;
use App\Models\UserAbo;
use App\Services\AboHelper;
use App\Models\ShoppingUser;
use App\Models\ShoppingOrder;
use App\Services\UserService;
use App\Models\ShoppingInstance;
use App\Http\Controllers\Controller;
class AboController extends Controller
{
private $instance = 'subscription';
private $yard;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:customers');
$this->yard = Yard::instance($this->instance);
}
public function myAbo()
{
$user = Auth::guard('customers')->user();
if (!$user->shopping_user_id) {
return view('portal.abo.my_abo_create', [
'user' => $user,
'no_shopping_user' => true,
'step' => 0,
]);
}
$shopping_user = ShoppingUser::findOrFail($user->shopping_user_id);
$user_abo = UserAbo::where('email', $shopping_user->billing_email)
->where('status', '>', 1)
->first();
return $user_abo
? view('portal.abo.my_abo', ['user_abo' => $user_abo])
: view('portal.abo.my_abo_create', [
'shopping_user' => $shopping_user,
'step' => 0,
]);
}
public function myAboCreate($step)
{
$user = Auth::guard('customers')->user();
if (!$user->shopping_user_id) {
abort(403, 'Unauthorized action.');
}
$shopping_user = ShoppingUser::findOrFail($user->shopping_user_id);
$data = $this->prepareAboCreateData($shopping_user, $step);
if(isset($data['checkout_url'])){
return redirect($data['checkout_url']);
}
return view('portal.abo.my_abo_create', $data);
}
private function prepareAboCreateData($shopping_user, $step)
{
$data = [
'shopping_user' => $shopping_user,
'basis_products' => Product::where('active', true)
->whereJsonContains('show_on', ['12'])
->orderBy('pos', 'ASC')
->get(),
'upgrade_products' => Product::where('active', true)
->whereJsonContains('show_on', ['13'])
->orderBy('pos', 'ASC')
->get(),
'step' => 0,
];
if(Request::get('action') == 'back') {
$step = $step - 2;
}
switch ($step) {
case 0:
$data['step'] = 0;
break;
case 1:
$this->initYard($shopping_user);
$data['step'] = 1;
break;
case 2:
UserService::setInstance($this->instance);
UserService::initCustomerYard($shopping_user, 'abo-ot-customer');
$data['step'] = 2;
break;
case 3:
UserService::setInstance($this->instance);
UserService::initCustomerYard($shopping_user, 'abo-ot-customer');
if(Request::get('action') == 'next'){
if (!$this->checkBasisProduct()) {
$data['error'] = __('abo.abo_error_basis_product');
$data['step'] = 2;
} else {
$data['step'] = 3;
}
}else{
$data['step'] = 3;
}
break;
case 4:
UserService::setInstance($this->instance);
UserService::initCustomerYard($shopping_user, 'abo-ot-customer');
$this->upgradeProductToCart();
$data['step'] = 4;
break;
case 5:
//chekout verarbeiten
UserService::setInstance($this->instance);
UserService::initCustomerYard($shopping_user, 'abo-ot-customer');
if(Request::get('action') == 'checkout'){
//checkout verarbeiten
if (!$this->preCheckCheckout()) {
$data['error'] = __('abo.abo_error_basis_product');
$data['step'] = 4;
} else {
$data['checkout_url'] = $this->processCheckout();
}
}
$data['step'] = 4;
break;
default:
abort(404, 'Page not found.');
}
return $data;
}
private function initYard($shopping_user)
{
$delivery_country = $shopping_user->getDeliveryCountry(true);
if (!$delivery_country) {
abort(404, 'No delivery country found, please edit your personal data.');
}
\Session::put('user_init_country', strtolower($delivery_country->code));
\Session::forget('user_init_country_options');
\Session::put('locale', strtolower(\App::getLocale()));
Shop::initUserShopLang($delivery_country, $this->instance);
}
private function preCheckCheckout(){
$result = false;
//alle inhlate des warenkorb
$cartItems = $this->yard->content();
foreach($cartItems as $item){
if(in_array(12, $item->options->show_on)){
$result = true;
}
}
return $result;
}
private function checkBasisProduct()
{
$data = Request::all();
$result = false;
if (!isset($data['base_product_qty'])) {
return false;
}
foreach ($data['base_product_qty'] as $product_id => $quantity) {
$product = Product::find($product_id);
if (!$product || intval($quantity) <= 0) {
continue;
}
$result = true;
$this->addProductToCart($product, $quantity);
}
return $result;
}
private function upgradeProductToCart(){
$data = Request::all();
$result = false;
if (!isset($data['upgrade_product_qty'])) {
return false;
}
foreach ($data['upgrade_product_qty'] as $product_id => $quantity) {
$product = Product::find($product_id);
if (!$product) {
continue;
}
$result = true;
$this->addProductToCart($product, $quantity);
}
return $result;
}
private function addProductToCart($product, $quantity)
{
// Suche nach dem Produkt im Warenkorb
$cartItems = $this->yard->search(function($item) use ($product) {
return $item->id === $product->id;
});
// Wenn die Menge 0 ist, entferne das Produkt
if ($quantity <= 0) {
foreach ($cartItems as $item) {
$this->yard->remove($item->rowId);
}
return;
}
$image = $product->images->first()->slug ?? '';
$price = $product->getPriceWith(
$this->yard->getUserTaxFree(),
false,
$this->yard->getUserCountry()
);
// Wenn das Produkt bereits im Warenkorb ist, aktualisiere die Menge
if ($cartItems->count() > 0) {
$cartItem = $cartItems->first();
$this->yard->update($cartItem->rowId, $quantity);
} else {
// Wenn das Produkt noch nicht im Warenkorb ist, füge es hinzu
$cartItem = $this->yard->add(
$product->id,
$product->getLang('name'),
$quantity,
$price,
false,
false,
[
'image' => $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
]
);
}
// $this->setProductTax($cartItem, $product);
$this->yard->reCalculateShippingPrice();
}
private function processCheckout(){
$user_shop = Util::getUserShop();
if(!$user_shop){
$user_shop = Util::getDefaultUserShop();
}
do {
$identifier = Util::getToken();
} while( ShoppingInstance::where('identifier', $identifier)->count() );
$data = [];
$data['is_from'] = 'shopping';
$data['user_price_infos'] = $this->yard->getUserPriceInfos();
ShoppingInstance::create([
'identifier' => $identifier,
'user_shop_id' => $user_shop->id,
'payment' => 1, //Customer Shop Payment
'subdomain' => url('/'),
'country_id' => $this->yard->getShippingCountryId(),
'language' => \App::getLocale(),
'shopping_data' => $data,
'back' => url()->previous(),
]);
$this->yard->store($identifier);
//add to DB
$path = route('checkout.checkout_card', ['identifier'=>$identifier]);
if(strpos($path, 'https') === false){
$path = str_replace('http', 'https', $path);
}
return $path;
}
}

View file

@ -0,0 +1,203 @@
<?php
namespace App\Http\Controllers\Portal\Auth;
use Carbon\Carbon;
use App\Services\Util;
use Illuminate\Support\Str;
use App\Models\ShoppingUser;
use Illuminate\Http\Request;
use App\Mail\MailOTPCustomer;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\ValidationException;
use App\Models\Customer; // Oder User, je nach Setup
use App\Models\OtpToken; // Zum Speichern/Prüfen des OTP
use Illuminate\Support\Facades\Hash; // Zum Hashen des Tokens
class LoginController extends Controller
{
// Zeigt das Formular zur Eingabe der E-Mail an
public function showLoginForm()
{
//wenn als Berater eingeloggt, dann zum Login wechseln
if(Auth::guard('user')->check()){
return redirect()->route('portal.change_login');
}
//wenn als Kunde eingeloggt, dann direkt zum Dashboard
if (Auth::guard('customers')->check()) {
return redirect()->route('portal.dashboard');
}
return view('portal.auth.login'); // Erstelle diese View
}
// Sendet das OTP
public function sendOtp(Request $request)
{
$request->validate(['email' => 'required|email']);
$email = $request->input('email');
// 1. Prüfen, ob die E-Mail im System bekannt ist über Kunden-Tabelle)
$customer = Customer::firstOrCreate(['email' => $email]); // Erstellt Kunden, wenn nicht vorhanden
if($customer && $customer->language){
\App::setLocale($customer->language);
}
// if (!$customerExists && !$orderExists) { // Oder nur eine Prüfung, je nach Logik
if (!$customer) { // Prüfung anhand des Customer-Models
throw ValidationException::withMessages([
'email' => __('auth.failed_customer'), // Generische Fehlermeldung
]);
}
// 2. Alten Token löschen (optional, aber empfohlen)
DB::table('otp_tokens')->where('email', $email)->delete();
// 3. OTP generieren (z.B. 6-stellige Zahl)
$otp = random_int(100000, 999999);
$expiresAt = Carbon::now()->addMinutes(10); // Gültigkeit z.B. 10 Minuten
// 4. OTP (gehasht!) speichern
DB::table('otp_tokens')->insert([
'email' => $email,
'token' => Hash::make((string)$otp), // WICHTIG: Token hashen!
'expires_at' => $expiresAt,
'created_at' => Carbon::now(),
]);
// 5. OTP per E-Mail senden (Notification oder Mailable verwenden)
try {
Mail::to($email)->locale(\App::getLocale())->send(new MailOTPCustomer($otp, $email));
} catch (\Exception $e) {
// Logge den Fehler
\Log::error('OTP Send Error: ' . $e->getMessage());
// Gib eine Fehlermeldung zurück, ohne Details preiszugeben
return back()->withErrors(['email' => 'Konnte E-Mail nicht senden. Bitte versuchen Sie es später erneut.'])->withInput();
}
// 6. Zum OTP-Eingabeformular weiterleiten (E-Mail in Session speichern oder als Parameter übergeben)
session(['otp_email' => $email]); // Explizit in Session speichern
return redirect()->route('portal.login.otp.form', ['email' => $email]); // E-Mail auch als Parameter übergeben
}
// Zeigt das Formular zur Eingabe des OTP an
public function showOtpForm(Request $request, $email = null, $otp = null)
{
//wenn als Berater eingeloggt, dann zum Login wechseln
if(Auth::guard('user')->check()){
return redirect()->route('portal.change_login');
}
//wenn als Kunde eingeloggt, dann zum Dashboard wechseln
if (Auth::guard('customers')->check()) {
return redirect()->route('portal.dashboard');
}
// E-Mail aus der Session holen (oder als Request-Parameter erwarten)
if($email) {
$email = $email;
} else {
$email = session('otp_email', $request->query('email'));
}
if (!$email) {
return redirect()->route('portal.login.form')->withErrors(['message' => 'Sitzung abgelaufen oder E-Mail fehlt.']);
}
// CSRF-Token regenerieren für neue Session
$request->session()->regenerateToken();
// Übergebe sowohl 'email' als auch 'otp' an die View
return view('portal.auth.verify-otp', ['email' => $email, 'otp_value' => $otp]); // Variable umbenannt zu otp_value für Klarheit
}
// Validiert das OTP und loggt den Kunden ein
public function verifyOtpAndLogin(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|numeric|digits:6', // An die Länge deines OTPs anpassen
]);
$email = $request->input('email');
$otpInput = $request->input('otp');
// 1. Gespeicherten OTP-Eintrag finden
$otpRecord = DB::table('otp_tokens')->where('email', $email)->first();
// 2. Prüfen ob Eintrag existiert, nicht abgelaufen ist und das OTP (Hash) übereinstimmt
if (!$otpRecord || Carbon::now()->gt($otpRecord->expires_at) || !Hash::check($otpInput, $otpRecord->token)) {
// Ungültiges oder abgelaufenes OTP
DB::table('otp_tokens')->where('email', $email)->delete(); // Ungültigen Token löschen
return back()->withErrors(['otp' => 'Ungültiges oder abgelaufenes Einmalpasswort.'])->withInput(['email' => $email]);
}
// 3. Kunden-Objekt finden (basierend auf dem Provider-Model)
$customer = Customer::where('email', $email)->first(); // Oder User::where('email', $email)->where('role','customer')->first();
if (!$customer) {
// Sollte eigentlich nicht passieren, wenn sendOtp korrekt funktioniert hat
DB::table('otp_tokens')->where('email', $email)->delete();
return back()->withErrors(['otp' => __('auth.failed')])->withInput(['email' => $email]);
}
// 4. Kunden einloggen über den 'customers'-Guard
Auth::guard('customers')->login($customer); // Loggt den gefundenen Kunden ein
// 5. Explizite Session-Speicherung
$request->session()->save();
// 6. OTP-Eintrag löschen
DB::table('otp_tokens')->where('email', $email)->delete();
// 7. Session Token regenerieren (statt komplette Session)
$request->session()->regenerate();
// 8. customer DB aktualisieren
$shopping_user = ShoppingUser::where('billing_email', $email)->latest()->first();
if($shopping_user){
$data = [
'name' => $shopping_user->billing_firstname . ' ' . $shopping_user->billing_lastname,
'shopping_user_id' => $shopping_user->id,
'member_id' => $shopping_user->member_id,
'number' => $shopping_user->number,
'language' => session('locale') ?? 'de',
];
$customer->update($data);
}else{
$data = [
'name' => __('portal.guest'),
'shopping_user_id' => null,
'member_id' => null,
'number' => null,
'language' => session('locale') ?? 'de',
];
$customer->update($data);
}
// 10. Zum Kunden-Dashboard weiterleiten
return redirect()->intended(route('portal.dashboard')); // intended() leitet zur ursprünglich angefragten Seite weiter
}
// Logout für Kunden
public function logout(Request $request)
{
$url = Util::getMyMivitaShopUrl();
Auth::guard('customers')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect($url);
}
// Logout für Berater
public function logoutChange(Request $request)
{
//$url = Util::getMyMivitaShopUrl();
$user_shop_domain = session('user_shop_domain');
$locale = session('locale');
Auth::guard('user')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
session(['user_shop_domain' => $user_shop_domain]);
session(['locale' => $locale]);
return redirect()->route('portal.login.form');
}
}

View file

@ -0,0 +1,99 @@
<?php
namespace App\Http\Controllers\Portal;
use Auth;
use Request;
use Validator;
use App\Models\ShoppingUser;
use App\Services\CustomerPriority;
use App\Http\Controllers\Controller;
class CustomerController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:customers');
}
public function myDataEdit()
{
$user = Auth::guard('customers')->user();
if($user->shopping_user_id){
$shopping_user = ShoppingUser::findOrFail($user->shopping_user_id);
}else{
$shopping_user = new ShoppingUser();
}
$data = [
'shopping_user' => $shopping_user,
'isAdmin' => false,
'isView' => 'customer',
];
return view('portal.customer.edit', $data);
}
public function myDataStore(){
$user = Auth::guard('customers')->user();
$data = Request::all();
if($data['action'] === 'shopping-user-store-new' || $data['action']==='shopping-user-store'){
$rules = array(
'billing_salutation' => 'required',
'billing_firstname'=>'required',
'billing_lastname'=>'required',
'billing_address'=>'required',
'billing_zipcode'=>'required',
'billing_city' => 'required',
'billing_country_id' => 'required',
);
if(!Request::get('same_as_billing')){
$rules = array_merge($rules, [
'shipping_firstname'=>'required',
'shipping_lastname'=>'required',
'shipping_address'=>'required',
'shipping_zipcode'=>'required',
'shipping_city' => 'required',
'shipping_salutation' => 'required',
'shipping_country_id' => 'required'
]);
}
$validator = Validator::make(Request::all(), $rules);
if ($validator->fails()) {
return back()->withErrors($validator)->withInput(Request::all());
}
}
$data['language'] = \App::getLocale();
$data['same_as_billing'] = isset($data['same_as_billing']) ? true : false;
$data['shipping_country_id'] = isset($data['shipping_country_id']) ? $data['shipping_country_id'] : $data['billing_country_id'];
if($user->shopping_user_id){
$shopping_user = ShoppingUser::findOrFail($user->shopping_user_id);
$shopping_user->fill($data);
$shopping_user->save();
}else{
$data['billing_email'] = $user->email;
$shopping_user = ShoppingUser::create($data);
$user->shopping_user_id = $shopping_user->id;
$user->save();
//kundenhoheit
CustomerPriority::checkOne(ShoppingUser::find($shopping_user->id), true);
}
\Session()->flash('alert-save', true);
return redirect(route('portal.my_data.edit'));
}
}

View file

@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Portal;
use App\Http\Controllers\Controller;
use App\Models\Product;
use App\Models\ShoppingPayment;
use App\User;
use Auth;
use Carbon\Carbon;
use Config;
use Request;
use Storage;
use Util;
class InController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
}
public function index()
{
if(Auth::guard('user')->check()){
return redirect(route('portal.change_login'));
}
if(!Auth::guard('customers')->check()){ // if () {
return redirect(route('portal.login.form'));
}
return redirect(route('portal.dashboard'));
}
public function changeLogin(){
if(Auth::guard('customers')->check()){
return redirect(route('portal.dashboard'));
}
if(Auth::guard('user')->check()){
return view('portal.auth.change');
}
return redirect(route('portal.login.form'));
}
public function dashboard()
{
if(!Auth::guard('customers')->check()){
return redirect(route('portal.login.form'));
}
$data = [
'user' => Auth::guard('customers')->user(),
'now' => Carbon::now(),
];
return view('portal.dashboard', $data);
}
public function loadingModal(){
$data = Request::all();
$response = "";
$status = false;
if(isset($data['action']) && $data['action'] === 'user-order-show-product'){
$product = Product::find($data['id']); //current user form order
$ret = view("admin.modal.show_product", compact('product', 'data'))->render();
return response()->json(['response' => $data, 'html'=>$ret, 'status'=>$status]);
}
$data = Request::get('data');
$target = Request::get('target');
if($data === "data_protection"){
$data = [
'modal' => true,
'user_shop' => true,
'isMivitaShop' => false,
];
$response = view('legal.data_protect_de', $data)->render();
}
if($data === "imprint"){
$data = [
'modal' => true,
'user_shop' => Util::getUserShop(),
];
$response = view('legal.imprint_de', $data)->render();
}
if($data === "shop_term_of_use"){
$data = [
'modal' => true,
'user_shop' => Util::getUserShop(),
];
$response = view('legal.shop_term_of_use_de', $data)->render();
}
if($data === "agb"){
$data = [
'modal' => true,
'user_shop' => Util::getUserShop(),
];
$response = view('legal.agb_de', $data)->render();
}
if(Request::ajax()) {
return response()->json(['response' => $response, 'target'=>$target]);
}
abort(404);
}
/* public function goToShop(){
if(!Auth::guard('customers')->check()){
return redirect(config('app.protocol') . config('app.domain') . config('app.tld_shop'));
}
$customer = Auth::guard('customers')->user();
//subdmain for member
$member = User::where('email', $customer->email)->first();
if($member){
return redirect(config('app.protocol') . $member->subdomain . config('app.tld_care'));
}
// $customer->member_id
// return redirect(config('app.protocol') . config('app.domain') . config('app.tld_shop'));
}*/
}

View file

@ -0,0 +1,116 @@
<?php
namespace App\Http\Controllers\Portal;
use App\Http\Controllers\Controller;
use App\Models\Product;
use App\Models\ShoppingOrder;
use App\Models\ShoppingUser;
use App\Services\Shop;
use App\Services\Util;
use Auth;
use Request;
use Validator;
use Yard;
class OrderController extends Controller
{
private $instance = 'webshop';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:customers');
}
public function myOrders()
{
$user = Auth::guard('customers')->user();
if($user->shopping_user_id){
$shopping_user = ShoppingUser::findOrFail($user->shopping_user_id);
$shopping_orders = $shopping_user->getAllOrdersByMember();
}else{
$shopping_user = new ShoppingUser();
$shopping_orders = [];
}
$data = [
'shopping_user' => $shopping_user,
'shopping_orders' => $shopping_orders,
];
return view('portal.order.my_orders', $data);
}
public function myOrderShow($id)
{
$user = Auth::guard('customers')->user();
$shopping_user = ShoppingUser::findOrFail($user->shopping_user_id);
$shopping_order = ShoppingOrder::findOrFail($id);
if($shopping_order->shopping_user_id != $user->shopping_user_id){
abort(403, 'Unauthorized action.');
}
return view('portal.order.my_order_show', [
'shopping_order' => $shopping_order,
'shopping_user' => $shopping_user,
]);
}
public function myOrderCreate($id)
{
$user = Auth::guard('customers')->user();
$shopping_order = ShoppingOrder::findOrFail($id);
if($shopping_order->shopping_user_id != $user->shopping_user_id){
abort(403, 'Unauthorized action.');
}
$shopping_user = ShoppingUser::findOrFail($user->shopping_user_id);
$delivery_country = $shopping_user->getDeliveryCountry(true);
\Session::put('user_init_country', strtolower($delivery_country->code));
\Session::forget('user_init_country_options');
\Session::put('locale', strtolower(\App::getLocale()));
Shop::initUserShopLang($delivery_country, $this->instance);
//init Yard
foreach($shopping_order->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product){
$this->addToCard($shopping_order_item->product_id, $shopping_order_item->qty);
}
}
$url = Util::getMyMivitaShopUrl("/user/card/show");
return redirect($url);
}
private function addToCard($id, $quantity = 1)
{
$product = Product::find($id);
if($product){
$image = "";
if($product->images->count()){
$image = $product->images->first()->slug;
}
$cartItem = Yard::instance($this->instance)
->add($product->id, $product->getLang('name'), $quantity,
$product->getPriceWith(Yard::instance($this->instance)->getUserTaxFree(), false, Yard::instance($this->instance)->getUserCountry()), false, false,
['image' => $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]);
if(Yard::instance($this->instance)->getUserTaxFree()){
Yard::setTax($cartItem->rowId, 0);
}else{
Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance($this->instance)->getUserCountry()));
}
Yard::instance($this->instance)->reCalculateShippingPrice();
\Session()->flash('show-card-after-add', true);
}
}
}

View file

@ -59,7 +59,6 @@ class ProductController extends Controller
public function store()
{
$data = Request::all();
$rules = array(
'name' => 'required',
);
@ -189,7 +188,7 @@ class ProductController extends Controller
return redirect(route('admin_product_edit', [$product->id]));
}
catch (Exception $e) {
catch ( \Exception $e) {
\Session()->flash('alert-danger', "Fehler".$e);
return redirect(route('admin_product_edit', [$product->id]));
}

View file

@ -0,0 +1,343 @@
<?php
namespace App\Http\Controllers;
use Auth;
use Request;
use App\Models\UserInvoice;
use App\Models\UserCredit;
use App\Services\HTMLHelper;
use App\Exports\UserTeamExport;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
class RevenueReportController extends Controller
{
public function __construct()
{
$this->middleware('admin');
}
public function index()
{
$this->setFilterVars();
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'revenue_summary' => $this->getRevenueSummary(),
'credit_summary' => $this->getCreditSummary()
];
return view('admin.revenue.index', $data);
}
public function export()
{
$this->setFilterVars();
$filter_year = session('revenue_filter_year');
// Get data like in the HTML view
$revenue_summary = $this->getRevenueSummary();
$credit_summary = $this->getCreditSummary();
$filename = "umsatz-gutschrift-bericht-{$filter_year}";
$columns = [];
// Umsätze Section Header
$columns[] = ['Typ' => 'UMSÄTZE', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
// Yearly Revenue Summary
if(isset($revenue_summary['yearly']) && $revenue_summary['yearly']->count() > 0) {
foreach($revenue_summary['yearly'] as $item) {
$columns[] = [
'Typ' => $item->period_label,
'Netto' => number_format($item->total_net, 2, ',', '.'),
'Steuer' => number_format($item->total_tax, 2, ',', '.'),
'Brutto' => number_format($item->total_gross, 2, ',', '.')
];
}
} else {
$columns[] = [
'Typ' => "Jahr {$filter_year}",
'Netto' => '0,00',
'Steuer' => '0,00',
'Brutto' => '0,00'
];
}
// Empty row
$columns[] = ['Typ' => '', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
// Monthly Revenue Breakdown
$columns[] = ['Typ' => 'MONATLICHE AUFSCHLÜSSELUNG UMSÄTZE', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
if(isset($revenue_summary['monthly']) && $revenue_summary['monthly']->count() > 0) {
foreach($revenue_summary['monthly'] as $item) {
$columns[] = [
'Typ' => $item->period_label,
'Netto' => number_format($item->total_net, 2, ',', '.'),
'Steuer' => number_format($item->total_tax, 2, ',', '.'),
'Brutto' => number_format($item->total_gross, 2, ',', '.')
];
}
} else {
$columns[] = [
'Typ' => 'Keine monatlichen Umsätze gefunden',
'Netto' => '',
'Steuer' => '',
'Brutto' => ''
];
}
// Two empty rows for separation
$columns[] = ['Typ' => '', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
$columns[] = ['Typ' => '', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
// Gutschriften Section Header
$columns[] = ['Typ' => 'GUTSCHRIFTEN', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
// Yearly Credit Summary
if(isset($credit_summary['yearly']) && $credit_summary['yearly']->count() > 0) {
foreach($credit_summary['yearly'] as $item) {
$columns[] = [
'Typ' => $item->period_label,
'Netto' => number_format($item->total_net, 2, ',', '.'),
'Steuer' => number_format($item->total_tax, 2, ',', '.'),
'Brutto' => number_format($item->total_gross, 2, ',', '.')
];
}
} else {
$columns[] = [
'Typ' => "Jahr {$filter_year}",
'Netto' => '0,00',
'Steuer' => '0,00',
'Brutto' => '0,00'
];
}
// Empty row
$columns[] = ['Typ' => '', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
// Monthly Credit Breakdown
$columns[] = ['Typ' => 'MONATLICHE AUFSCHLÜSSELUNG GUTSCHRIFTEN', 'Netto' => '', 'Steuer' => '', 'Brutto' => ''];
if(isset($credit_summary['monthly']) && $credit_summary['monthly']->count() > 0) {
foreach($credit_summary['monthly'] as $item) {
$columns[] = [
'Typ' => $item->period_label,
'Netto' => number_format($item->total_net, 2, ',', '.'),
'Steuer' => number_format($item->total_tax, 2, ',', '.'),
'Brutto' => number_format($item->total_gross, 2, ',', '.')
];
}
} else {
$columns[] = [
'Typ' => 'Keine monatlichen Gutschriften gefunden',
'Netto' => '',
'Steuer' => '',
'Brutto' => ''
];
}
$headers = ['Zeitraum', 'Netto (€)', 'Steuer (€)', 'Brutto (€)'];
return Excel::download(new UserTeamExport($columns, $headers), $filename . '.xlsx');
}
private function setFilterVars()
{
if (!session('revenue_filter_month')) {
session(['revenue_filter_month' => intval(date('m'))]);
}
if (!session('revenue_filter_year')) {
session(['revenue_filter_year' => intval(date('Y'))]);
}
if(!session('revenue_filter_type')){
session(['revenue_filter_type' => 'year']);
}
if (Request::get('revenue_filter_month')) {
session(['revenue_filter_month' => Request::get('revenue_filter_month')]);
}
if (Request::get('revenue_filter_year')) {
session(['revenue_filter_year' => Request::get('revenue_filter_year')]);
}
if (Request::get('revenue_filter_type')) {
session(['revenue_filter_type' => Request::get('revenue_filter_type')]);
}
}
private function getRevenueSummary()
{
$year = session('revenue_filter_year');
return [
'yearly' => $this->getRevenueByYear($year),
'monthly' => $this->getRevenueByMonthsInYear($year)
];
}
private function getCreditSummary()
{
$year = session('revenue_filter_year');
return [
'yearly' => $this->getCreditByYear($year),
'monthly' => $this->getCreditByMonthsInYear($year)
];
}
private function getRevenueByYear($year)
{
return UserInvoice::join('shopping_orders', 'user_invoices.shopping_order_id', '=', 'shopping_orders.id')
->selectRaw("
{$year} as year,
CONCAT('Jahr ', {$year}) as period_label,
SUM(shopping_orders.subtotal_ws) as total_net,
SUM(shopping_orders.tax) as total_tax,
SUM(shopping_orders.total_shipping) as total_gross
")
->where('user_invoices.year', $year)
->where('user_invoices.cancellation', false)
->groupBy(DB::raw('1'))
->get();
}
private function getRevenueByMonth($year, $month)
{
return UserInvoice::join('shopping_orders', 'user_invoices.shopping_order_id', '=', 'shopping_orders.id')
->selectRaw("
{$year} as year,
{$month} as month,
CONCAT(CASE {$month}
WHEN 1 THEN 'Januar'
WHEN 2 THEN 'Februar'
WHEN 3 THEN 'März'
WHEN 4 THEN 'April'
WHEN 5 THEN 'Mai'
WHEN 6 THEN 'Juni'
WHEN 7 THEN 'Juli'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'Oktober'
WHEN 11 THEN 'November'
WHEN 12 THEN 'Dezember'
END, ' ', {$year}) as period_label,
SUM(shopping_orders.subtotal_ws) as total_net,
SUM(shopping_orders.tax) as total_tax,
SUM(shopping_orders.total_shipping) as total_gross
")
->where('user_invoices.year', $year)
->where('user_invoices.month', $month)
->where('user_invoices.cancellation', false)
->groupBy(DB::raw('1'))
->get();
}
private function getRevenueByMonthsInYear($year)
{
return UserInvoice::join('shopping_orders', 'user_invoices.shopping_order_id', '=', 'shopping_orders.id')
->selectRaw("
user_invoices.year,
user_invoices.month,
CONCAT(CASE user_invoices.month
WHEN 1 THEN 'Januar'
WHEN 2 THEN 'Februar'
WHEN 3 THEN 'März'
WHEN 4 THEN 'April'
WHEN 5 THEN 'Mai'
WHEN 6 THEN 'Juni'
WHEN 7 THEN 'Juli'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'Oktober'
WHEN 11 THEN 'November'
WHEN 12 THEN 'Dezember'
END, ' ', user_invoices.year) as period_label,
SUM(shopping_orders.subtotal_ws) as total_net,
SUM(shopping_orders.tax) as total_tax,
SUM(shopping_orders.total_shipping) as total_gross
")
->where('user_invoices.year', $year)
->where('user_invoices.cancellation', false)
->groupBy('user_invoices.year', 'user_invoices.month')
->orderBy('user_invoices.month')
->get();
}
private function getCreditByYear($year)
{
return UserCredit::selectRaw("
{$year} as year,
CONCAT('Jahr ', {$year}) as period_label,
SUM(net) as total_net,
SUM(tax) as total_tax,
SUM(total) as total_gross
")
->where('year', $year)
->where('cancellation', false)
->groupBy(DB::raw('1'))
->get();
}
private function getCreditByMonth($year, $month)
{
return UserCredit::selectRaw("
{$year} as year,
{$month} as month,
CONCAT(CASE {$month}
WHEN 1 THEN 'Januar'
WHEN 2 THEN 'Februar'
WHEN 3 THEN 'März'
WHEN 4 THEN 'April'
WHEN 5 THEN 'Mai'
WHEN 6 THEN 'Juni'
WHEN 7 THEN 'Juli'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'Oktober'
WHEN 11 THEN 'November'
WHEN 12 THEN 'Dezember'
END, ' ', {$year}) as period_label,
SUM(net) as total_net,
SUM(tax) as total_tax,
SUM(total) as total_gross
")
->where('year', $year)
->where('month', $month)
->where('cancellation', false)
->groupBy(DB::raw('1'))
->get();
}
private function getCreditByMonthsInYear($year)
{
return UserCredit::selectRaw("
year,
month,
CONCAT(CASE month
WHEN 1 THEN 'Januar'
WHEN 2 THEN 'Februar'
WHEN 3 THEN 'März'
WHEN 4 THEN 'April'
WHEN 5 THEN 'Mai'
WHEN 6 THEN 'Juni'
WHEN 7 THEN 'Juli'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'Oktober'
WHEN 11 THEN 'November'
WHEN 12 THEN 'Dezember'
END, ' ', year) as period_label,
SUM(net) as total_net,
SUM(tax) as total_tax,
SUM(total) as total_gross
")
->where('year', $year)
->where('cancellation', false)
->groupBy('year', 'month')
->orderBy('month')
->get();
}
}

View file

@ -373,14 +373,14 @@ class SalesController extends Controller
if(isset($data['action'])){
if($data['action'] === 'create_invoice'){
$shopping_order = ShoppingOrder::findOrFail($data['id']);
if($shopping_order->mode === 'live'){
$invoice_repo = new InvoiceRepository($shopping_order);
if($shopping_order->isInvoice()){
$invoice_repo->update($data);
}else{
$invoice_repo->createAndSalesVolume($data);
}
$invoice_repo = new InvoiceRepository($shopping_order);
if($shopping_order->isInvoice()){
$invoice_repo->update($data);
}else{
$invoice_repo->createAndSalesVolume($data);
}
if(isset($data['view']) && $data['view'] === 'sales_customer'){
return redirect(route('admin_sales_customers_detail', [$shopping_order->id]));
}

View file

@ -37,9 +37,11 @@ class ShippingController extends Controller
if($shipping_id === "new"){
$shipping = new Shipping();
$shipping->active = 1;
// For new shipping objects, create an empty collection for countries
$shipping->setRelation('countries', collect());
}else{
$shipping = Shipping::findOrFail($shipping_id);
$shipping = Shipping::with(['countries.country'])->findOrFail($shipping_id);
}
$data = [

View file

@ -1,153 +0,0 @@
<?php
namespace App\Http\Controllers;
use Auth;
use Request;
use App\User;
use Carbon\Carbon;
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 TaxAdvisorController extends Controller
{
public function __construct()
{
$this->middleware('admin');
}
public function index()
{
$this->setFilterVars();
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2023),
];
return view('admin.payment.taxadvisor', $data);
}
public function download(){
if(Request::get('action') === "export"){
$objects = $this->initSearch(false);
$columns = [];
$filename = "mivita-absatzmengen-".session('payment_taxadvisor_filter_month').'_'.session('payment_taxadvisor_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('payment_taxadvisor_filter_month')){
session(['payment_taxadvisor_filter_month' => intval(date('m'))]);
}
if(!session('payment_taxadvisor_filter_year')){
session(['payment_taxadvisor_filter_year' => intval(date('Y'))]);
}
if(Request::get('payment_taxadvisor_filter_month')){
session(['payment_taxadvisor_filter_month' => Request::get('payment_taxadvisor_filter_month')]);
}
if(Request::get('payment_taxadvisor_filter_year')){
session(['payment_taxadvisor_filter_year' => Request::get('payment_taxadvisor_filter_year')]);
}
}
private function initSearch($returnColl = true)
{
$this->setFilterVars();
$date_start = Carbon::parse('01.'.session('payment_taxadvisor_filter_month').'.'.session('payment_taxadvisor_filter_year'))->format('Y-m-d');
$date_end = Carbon::parse('01.'.session('payment_taxadvisor_filter_month').'.'.session('payment_taxadvisor_filter_year'))->endOfMonth()->format('Y-m-d');
$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();
}
}

View file

@ -51,7 +51,6 @@ class CustomerController extends Controller
'shopping_user' => $shopping_user,
'isAdmin' => false,
'isView' => 'customer',
];
return view('user.customer.detail', $data);
}

View file

@ -232,6 +232,8 @@ class MembershipController extends Controller
return back();
}
\Session()->flash('alert-error', __('msg.error_checkbox_not_confirm'));
return back();
}

View file

@ -344,6 +344,7 @@ class OrderController extends Controller
*/
private function processUserPayment($user, $identifier, $data, $id, $for)
{
Shop::deleteCheckoutInstance();
ShoppingInstance::create([
'identifier' => $identifier,
'user_shop_id' => 1, // is first faker shop for buy intern

View file

@ -1,674 +0,0 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Mail\MailCustomPaymet;
use App\Models\Product;
use App\Models\ShippingCountry;
use App\Models\ShoppingInstance;
use App\Models\ShoppingOrder;
use App\Models\ShoppingUser;
use App\Models\UserHistory;
use App\Services\AboHelper;
use App\Services\OrderPaymentService;
use App\Services\Payment;
use App\Services\Shop;
use App\Services\UserService;
use App\Services\Util;
use App\User;
use Auth;
use Illuminate\Support\Facades\Mail;
use Request;
use Validator;
use Yard;
class OrderController extends Controller
{
public function __construct()
{
$this->middleware('active.account');
}
public function index()
{
$data = [
];
return view('user.order.index', $data);
}
public function detail($id)
{
$user = User::find(\Auth::user()->id);
$shopping_order = ShoppingOrder::findOrFail($id);
if($shopping_order->auth_user_id !== $user->id){
abort(404);
}
if( $shopping_order->payment_for === 6 || $shopping_order->payment_for === 7){
return redirect(route('user_shop_order_detail', [$shopping_order->id]));
abort(403, 'Kundenbestellung');
}
$shopping_order->getLastShoppingPayment();
$data = [
'shopping_order' => $shopping_order,
'isAdmin' => false,
];
return view('user.order.detail', $data);
}
public function ordersDatatable(){
$user = User::find(\Auth::user()->id);
$query = ShoppingOrder::with('shopping_user', 'shopping_payments')->select('shopping_orders.*')->where('auth_user_id', '=', $user->id)->where('txaction', '!=', NULL);
return \DataTables::eloquent($query)
->addColumn('id', function (ShoppingOrder $ShoppingOrder) {
return '<a href="'.route('user_order_detail', [$ShoppingOrder->id]).'" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('created_at', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->created_at->format("d.m.Y");
})
->addColumn('txaction', function (ShoppingOrder $ShoppingOrder) {
return Payment::getShoppingOrderBadge($ShoppingOrder);
})
->addColumn('total_shipping', function (ShoppingOrder $ShoppingOrder) {
return '<span class="no-line-break">'.$ShoppingOrder->getFormattedTotalShipping()." €</span>";
})
->addColumn('payment', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->getLastShoppingPayment('getPaymentType');
})
->addColumn('shipped', function (ShoppingOrder $ShoppingOrder) {
if($ShoppingOrder->payment_for === 8){
return '<button type="button" class="btn btn-xs btn-info btn-round" data-toggle="modal" data-target="#modals-load-content"
data-id="'.$ShoppingOrder->id.'"
data-action="shop-user-order-shipping-detail"
data-back=""
data-modal="modal-xl"
data-init_from="user"
data-route="'.route('modal_load').'"><span class="fa fa-eye"></span></button>';
}
return '<span class="badge badge-pill badge-'.$ShoppingOrder->getShippedColor().'">'.$ShoppingOrder->getShippedType().'</span>';
})
->addColumn('payment_for', function (ShoppingOrder $ShoppingOrder) {
return Payment::getPaymentForBadge($ShoppingOrder);
})
->addColumn('invoice', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->isInvoice() ? '<span class="no-line-break"><a href="'.route('storage_file', [$ShoppingOrder->id, 'invoice', 'download']).'" class="btn btn-primary btn-xs"><i class="fa fa-download"></i></a>
<a href="'.route('storage_file', [$ShoppingOrder->id, 'invoice', 'stream']).'" target="_blank" class="btn btn-warning btn-xs"><i class="fa fa-eye"></i></a></span>' : '-';
})
->addColumn('reference', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->getLastShoppingPayment('reference');
})
->orderColumn('id', 'id $1')
->orderColumn('txaction', 'txaction $1')
->orderColumn('shipped', 'shipped $1')
->orderColumn('total_shipping', 'total_shipping $1')
->orderColumn('payment_for', 'payment_for $1')
->rawColumns(['id', 'txaction', 'payment_for', 'total_shipping', 'invoice', 'shipped'])
->make(true);
}
/*
$for = me, ot-member, ot-customer, abo-ot-member, abo-ot-customer, abo-me
*/
public function delivery($for, $id=null)
{
$user = User::find(\Auth::user()->id);
$shopping_user = null;
$delivery_id = null;
if(strpos($for, 'ot') !== false){ //ot-member, ot-customer abo-ot-member, abo-ot-customer,
$shopping_user = Shop::checkShoppingUser($id, $user);
$delivery_id = $shopping_user->id;
if(!Shop::checkShoppingCountry($for, $delivery_id) && !\Session()->has('custom-error')){
$country = Shop::getDeliveryCountry($for, $delivery_id);
\Session()->flash('custom-error', $country.": ".__('validation.custom.shipping_not_found'));
return redirect(route('user_order_my_delivery', [$for, $delivery_id]));
}
if($for === 'abo-ot-customer'){
//check if user has an Abo
if(AboHelper::hasAboByEmail($shopping_user->billing_email) && !\Session()->has('custom-error')){
\Session()->flash('custom-error', __('abo.error_email_has_abo', ['email' => $shopping_user->billing_email]));
return redirect(route('user_order_my_delivery', [$for, $delivery_id]));
}
}
}
if(Request::get('action') === 'next'){
Yard::instance('shopping')->destroy();
if(strpos(Request::get('switchers-radio-is-for'), 'ot') !== false){
$delivery_id = $id;
}
return redirect(route('user_order_my_list', [Request::get('switchers-radio-is-for'), $delivery_id]));
}
$data = [
'shopping_user' => $shopping_user,
'isAdmin' => false,
'isView' => 'customer',
'for' => $for,
'delivery_id' => $delivery_id,
];
return view('user.order.delivery', $data);
}
public function list($for, $id=null)
{
$user = User::find(\Auth::user()->id);
if($for === 'abo-me' && AboHelper::userHasAbo($user)){
abort(403, 'User has an Abo. Cannot order.');
}
$shopping_user = null;
$delivery_id = null;
if(strpos($for, 'ot') !== false){ //ot-member, ot-customer abo-ot-member, abo-ot-customer,
$shopping_user = Shop::checkShoppingUser($id, $user);
$delivery_id = $shopping_user->id;
}
if($for === 'ot-customer' || $for === 'abo-ot-customer'){
//Liederung an (abo-) ot-customer (Kunden) Zahlung und Rechnung geht an Kunden
UserService::initCustomerYard($shopping_user, $for);
}else{
//Lieferung an user oder (abo-) ot-member (Kunden) rechnung geht an User
//lieferland und rechnungsland prüfen
$shipping_country_id = Shop::checkShoppingCountry($for, $id);
if(!$shipping_country_id){
$country = Shop::getDeliveryCountry($for, $id);
\Session()->flash('custom-error', $country.": ".__('validation.custom.shipping_not_found'));
return redirect(route('user_order_my_delivery', [$for, $delivery_id]));
}
UserService::initUserYard($user, $shipping_country_id, $for);
}
$data = [
'shopping_user' => $shopping_user,
'user' => $user,
'isAdmin' => false,
'isView' => 'customer',
'for' => $for,
'template' => str_replace('abo-', '', $for),
'delivery_id' => $delivery_id,
'is_abo' => strpos($for, 'abo') !== false,
'comp_products' => Shop::getCompProducts($for),
];
return view('user.order.list', $data);
}
public function payment($for, $id=null){
$data = Request::all();
$user = User::find(Auth::user()->id);
$rules = array(
'shipping_salutation' => 'required',
'shipping_firstname'=>'required',
'shipping_lastname'=>'required',
'shipping_address'=>'required',
'shipping_zipcode'=>'required',
'shipping_city' => 'required',
'shipping_state' => 'required',
);
$validator = Validator::make(Request::all(), $rules);
if ($validator->fails()) {
return back()->withErrors($validator)->withInput(Request::all());
}
//hier prüfen, ob versand etc richtig berechnet wurde
$this->checkSendYardForPayment($data, $id);
if(Yard::instance('shopping')->getNumComp() > 0){
if(!isset($data['switchers-comp-product'])){
$validator->errors()->add('switchers-comp-product', __('msg.please_select_compensation_product'));
}else{
if(!is_array($data['switchers-comp-product'])){
$validator->errors()->add('switchers-comp-product', __('msg.please_select_compensation_product'));
}else{
if(count($data['switchers-comp-product']) !== Yard::instance('shopping')->getNumComp()){
$validator->errors()->add('switchers-comp-product', __('mdg.please_select_count_compensation_products', ['count'=>Yard::instance('shopping')->getNumComp()]));
}
}
}
if ($validator->errors()->count()) {
return back()->withErrors($validator)->withInput(Request::all());
}
}
do {
$identifier = Util::getToken();
} while( ShoppingInstance::where('identifier', $identifier)->count() );
$data['is_from'] = 'user_order';
$data['is_for'] = $for;
$data['is_abo'] = $data['is_abo'] ?? 0;
$data['abo_interval'] = $data['abo_interval'] ?? 0;
$data['shopping_user_id'] = $id;
$data['user_price_infos'] = Yard::instance('shopping')->getUserPriceInfos();
unset($data['quantity']);
unset($data['_token']);
$data['mode'] = config('app.mode') === 'test' ? 'test' : 'live';
if($for === 'ot-customer' || $for === 'abo-ot-customer'){
$shopping_instance = ShoppingInstance::create([
'identifier' => $identifier,
'user_shop_id' => $user->shop->id,
'payment' => 6, //Berater Shop to Customer Shop
'subdomain' => $user->shop->getSubdomain(),
'country_id' => Yard::instance('shopping')->getShippingCountryId(),
'language' => \App::getLocale(),
'amount' => Yard::instance('shopping')->totalWithShipping(2, '.', ''),
'status' => 0,
'shopping_user_id' => $id,
'shopping_data' => $data,
'back' => url()->previous(),
]);
Yard::instance('shopping')->store($identifier);
$yard_shopping_items = OrderPaymentService::getRestoredYardShoppingItems($shopping_instance);
// send Mail to Customer
$this->customPaymentSendMail($user, $identifier, $yard_shopping_items, $data);
UserHistory::create(['user_id' => $user->id, 'action'=>'user_order_customer', 'status'=>1, 'product_id'=>null, 'identifier'=>$identifier, 'is_abo'=>$data['is_abo']]);
//eine Abschließen bestellseite für den User + Link zum Kunden Shop + Mail an den Kunden / Berater
return redirect(route('user_order_my_custom_payment', ['identifier'=>$identifier]));
}else{
ShoppingInstance::create([
'identifier' => $identifier,
'user_shop_id' => 1, //is first faker shop for buy intern
'auth_user_id' => Auth::user()->id,
'payment' => 2, //Berater Shop
'subdomain' => url('/'),
'country_id' => Yard::instance('shopping')->getShippingCountryId(),
'language' => \App::getLocale(),
'amount' => Yard::instance('shopping')->totalWithShipping(2, '.', ''),
'status' => 0,
'shopping_user_id' => $id,
'shopping_data' => $data,
'back' => url()->previous(),
]);
Yard::instance('shopping')->store($identifier);
$path = route('checkout.checkout_card', ['identifier'=>$identifier]);
UserHistory::create(['user_id' => $user->id, 'action'=>'user_order_payment', 'status'=>1, 'product_id'=>null, 'identifier'=>$identifier, 'is_abo'=>$data['is_abo']]);
//$path = str_replace('http', 'https', $path);
return redirect()->secure($path);
}
}
private function checkSendYardForPayment($data, $id){
$user = User::find(\Auth::user()->id);
$shopping_user = null;
if(strpos($data['shipping_is_for'], 'ot') !== false){
$shopping_user = Shop::checkShoppingUser($id, $user);
}
$shipping_country_id = Shop::checkShoppingCountry($data['shipping_is_for'], $id);
if(!$shipping_country_id){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'no shipping_country_id found | Yard identifier: '.$identifier, $data);
abort(403, __('msg.shipping_country_was_not_found'));
}
//must be the same shipping country
if($shipping_country_id != Yard::instance('shopping')->getShippingCountryId()){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'shipping_country_id is not the same from Yard | Yard identifier: '.$identifier, $data);
abort(403, __('msg.shipping_country_was_not_correctly'));
}
if($data['shipping_is_for'] !== 'ot-customer'){
if(Yard::instance('shopping')->shipping_free){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'Yard can by not shipping_free | Yard identifier: '.$identifier, $data);
abort(403, __('msg.shopping_cart_was_shipping_free'));
}
}
if($data['shipping_is_for'] === 'ot-customer'){
if(!$user->shop){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'User has no Shop for an User to Customer order| Yard identifier: '.$identifier, $data);
abort(403, __('msg.shopping_cart_was_not_user_shop'));
}
}
$shipping_price = Shop::getShippingPriceByShippingCountryId($shipping_country_id, Yard::instance('shopping')->weight());
dump($shipping_price);
//for other and has weight - check
if(strpos($data['shipping_is_for'], 'ot') !== false && $data['shipping_is_for'] !== 'ot-customer' && Yard::instance('shopping')->weight() > 0){
if(!Yard::instance('shopping')->getShippingPrice() || Yard::instance('shopping')->getShippingPrice() == 0){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'Yard OT shipping_price is 0 or | Yard identifier: '.$identifier, $data);
abort(403, __('msg.shipping_cost_cannot_be_0'));
}
if(Yard::instance('shopping')->getShippingPrice() != $shipping_price->price){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'Yard OT shipping_price is not the same from shipping_price | Yard identifier: '.$identifier, $data);
abort(403, __('msg.shipping_costs_were_not_calculated_correctly'));
}
}
if(($data['shipping_is_for'] == 'me' || $data['shipping_is_for'] == 'abo-me') && Yard::instance('shopping')->weight() > 0){
if(!Yard::instance('shopping')->getShippingPrice() || Yard::instance('shopping')->getShippingPrice() == 0){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'Yard ME shipping_price is 0 or | Yard identifier: '.$identifier, $data);
abort(403, __('msg.shipping_cost_cannot_be_0'));
}
dump(Yard::instance('shopping')->getShippingPrice());
dump($shipping_price->price_comp);
dump(Yard::instance('shopping')->getNumComp());
dump($shipping_price->num_comp);
dd($data) ;
if(Yard::instance('shopping')->getShippingPrice() != $shipping_price->price_comp){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'Yard ME shipping_price is not the same from shipping_price | Yard identifier: '.$identifier, $data);
abort(403, __('msg.shipping_costs_were_not_calculated_correctly'));
}
if(Yard::instance('shopping')->getNumComp() != $shipping_price->num_comp){
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
Yard::instance('shopping')->store($identifier);
$data['user_id'] = Auth::user()->id;
$data['shopping_user_id'] = $id;
\App\Services\MyLog::writeLog('payment', 'error', 'Yard num_comp is 0 | Yard identifier: '.$identifier, $data);
abort(403, __('msg.compensation_products_cannot_be_0'));
}
}
}
public function datatable(){
if(Request::get('shipping_is_for') === 'me' || Request::get('shipping_is_for') === 'abo-me'){
$show_on_ids = Request::get('is_abo') ? ['12', '13'] : ['2'];
$query = Product::with('product_buyings')
->select('products.*')->where('products.active', true)
->where(function($q) use ($show_on_ids) {
foreach($show_on_ids as $id) {
$q->orWhereJsonContains('show_on', $id);
}
})
->orderByRaw("CASE
WHEN JSON_CONTAINS(show_on, ?, '$') THEN 1
WHEN JSON_CONTAINS(show_on, ?, '$') THEN 2
ELSE 3 END",
[$show_on_ids[0], isset($show_on_ids[1]) ? $show_on_ids[1] : $show_on_ids[0]]);
}else{
$show_on_ids = Request::get('is_abo') ? ['12', '13'] : ['3'];
$query = Product::select('products.*')
->where('active', true)
->where(function($q) use ($show_on_ids) {
foreach($show_on_ids as $id) {
$q->orWhereJsonContains('show_on', $id);
}
})
->orderByRaw("CASE
WHEN JSON_CONTAINS(show_on, ?, '$') THEN 1
WHEN JSON_CONTAINS(show_on, ?, '$') THEN 2
ELSE 3 END",
[$show_on_ids[0], isset($show_on_ids[1]) ? $show_on_ids[1] : $show_on_ids[0]]);
}
return \DataTables::eloquent($query)
->addColumn('product', function (Product $product) {
$cartItem = Yard::instance('shopping')->getCartItemByProduct($product->id);
$qty = isset($cartItem->qty) ? $cartItem->qty : 0;
$rowId = isset($cartItem->rowId) ? $cartItem->rowId : '';
return '<strong>'.$product->getLang('name').'</strong><br>
<div class="no-line-break input-group-min-w">
<div class="input-group d-inline-flex w-auto">
<span class="input-group-prepend">
<button type="button" class="btn btn-secondary icon-btn md-btn-extra remove-product-basket" data-row-id="'.$rowId.'" data-product-id="'.$product->id.'">-</button>
</span>
<input type="text" class="form-control text-center input-extra table-input-event-onchange" name="product_qty_'.$product->id.'" data-row-id="'.$rowId.'" data-product-id="'.$product->id.'" value="'.$qty.'">
<span class="input-group-append">
<button type="button" class="btn btn-secondary icon-btn md-btn-extra add-product-basket" data-row-id="'.$rowId.'" data-product-id="'.$product->id.'">+</button>
</span>
</div>
</div>';
})
->addColumn('abo', function (Product $product) {
return AboHelper::getAboTypeBadge(AboHelper::getAboShowOn($product));
})
/*
->addColumn('add_card', function (Product $product) {
return '<button type="button" class="btn btn-sm btn-md-extra btn-secondary add-product-basket" data-product-id="'.$product->id.'">
<strong>&euro; '.$product->getFormattedPriceWith().'</strong>&nbsp; +<span class="ion ion-md-cart"></span>
</button>';
})
->addColumn('quantity', function (Product $product) {
$cartItem = Yard::instance('shopping')->getCartItemByProduct($product->id);
$qty = isset($cartItem->qty) ? $cartItem->qty : 0;
$rowId = isset($cartItem->rowId) ? $cartItem->rowId : '';
return '<div class="no-line-break input-group-min-w">
<div class="input-group d-inline-flex w-auto">
<span class="input-group-prepend">
<button type="button" class="btn btn-secondary icon-btn md-btn-extra remove-product-basket" data-row-id="'.$rowId.'" data-product-id="'.$product->id.'">-</button>
</span>
<input type="text" class="form-control text-center input-extra table-input-event-onchange" name="product_qty_'.$product->id.'" data-row-id="'.$rowId.'" data-product-id="'.$product->id.'" value="'.$qty.'">
<span class="input-group-append">
<button type="button" class="btn btn-secondary icon-btn md-btn-extra add-product-basket" data-row-id="'.$rowId.'" data-product-id="'.$product->id.'">+</button>
</span>
</div>
</div>';
})*/
->addColumn('picture', function (Product $product) {
if(count($product->images)){
return '<img class="img-fluid img-extra" alt="" src="'.route('product_image', [$product->images->first()->slug]).'">';
}
return "";
})
->addColumn('price_net', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(true, true, Yard::instance('shopping')->getUserCountry()). " €</span>".'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(true, true, Yard::instance('shopping')->getUserCountry()).'</span>';
})
->addColumn('price_gross', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(false, true, Yard::instance('shopping')->getUserCountry()). " €</span>".'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(false, true, Yard::instance('shopping')->getUserCountry()).'</span>';
})
->addColumn('price_vk_gross', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(false, false, Yard::instance('shopping')->getUserCountry()). " €</span>".'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(false, false, Yard::instance('shopping')->getUserCountry()).'</span>';
})
->addColumn('customer_price_net', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(true, false, Yard::instance('shopping')->getUserCountry()). " €</span>".'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(true, false, Yard::instance('shopping')->getUserCountry()).'</span>';
})
->addColumn('customer_price_gross', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(false, false, Yard::instance('shopping')->getUserCountry()). " €</span>".'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(false, false, Yard::instance('shopping')->getUserCountry()).'</span>';
})
->addColumn('my_commission_net', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(true, false, Yard::instance('shopping')->getUserCountry(), true). " €</span>".'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(true, false, Yard::instance('shopping')->getUserCountry(), true).'</span>';
})
->addColumn('action', function (Product $product) {
return '<button class="btn btn-default btn-sm icon-btn md-btn-flat product-tooltip" title="details" data-modal="modal-lg"
data-toggle="modal" data-target="#modals-load-content" data-id="'.$product->id.'" data-route="'.route('modal_load').'"
data-action="user-order-show-product" data-view="customer"><i class="ion ion-md-eye"></i></button>';
})
->filterColumn('product', function($query, $keyword) {
if($keyword != ""){
$query->where('name', 'LIKE', '%'.$keyword.'%');
}
})
->orderColumn('name', 'name $1')
->orderColumn('product', 'name $1')
->orderColumn('number', 'number $1')
->orderColumn('points', 'points $1')
->orderColumn('price_net', 'price_net $1')
->orderColumn('price_gross', 'price_gross $1')
->orderColumn('price_vk_gross', 'price $1')
->orderColumn('customer_price_net', 'price $1')
->orderColumn('customer_price_gross', 'price $1')
->orderColumn('my_commission_net', 'price $1')
->orderColumn('contents_total', 'contents_total $1')
->orderColumn('weight', 'weight $1')
->orderColumn('abo', 'show_on $1')
->rawColumns(['add_card', 'price_net', 'price_gross', 'price_vk_gross', 'customer_price_net', 'customer_price_gross', 'my_commission_net', 'product', 'quantity', 'picture', 'abo', 'action'])
->make(true);
}
public function performRequest(){
if(Request::ajax()) {
$data = Request::all();
$is_for = isset($data['shipping_is_for']) ? $data['shipping_is_for'] : 'ot-member';
$data['for'] = $is_for;
$data['comp_products'] = Shop::getCompProducts($is_for);
if($data['action'] === 'updateCart' && isset($data['product_id'])){
if($product = Product::find($data['product_id'])){
$image = "";
if($product->images->count()){
$image = $product->images->first()->slug;
}
//get the card item
if($is_for === 'ot-customer' || $is_for === 'abo-ot-customer'){
$cartItem = Yard::instance('shopping')
->add($product->id, $product->getLang('name'), 1,
round($product->getPriceWith(Yard::instance('shopping')->getUserTaxFree(), false, Yard::instance('shopping')->getUserCountry()), 1), false, false,
['image' => $image, 'slug' => $product->slug, 'weight' => $product->weight, 'points' => $product->points, 'no_commission' => $product->no_commission, 'show_on' => $product->show_on]);
}else{
$cartItem = Yard::instance('shopping')
->add($product->id, $product->getLang('name'), 1,
$product->getPriceWith(Yard::instance('shopping')->getUserTaxFree(), true, Yard::instance('shopping')->getUserCountry()), false, false,
['image' => $image, 'slug' => $product->slug, 'weight' => $product->weight, 'points' => $product->points, 'no_commission' => $product->no_commission, 'show_on' => $product->show_on]);
}
if(Yard::instance('shopping')->getUserTaxFree()){
Yard::setTax($cartItem->rowId, 0);
}else{
Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance('shopping')->getUserCountry()));
}
if(isset($data['qty']) && $data['qty'] > 0){
Yard::instance('shopping')->update($cartItem->rowId, $data['qty']);
}else{
//if 0 get the item by qty:1 and remove it
Yard::instance('shopping')->remove($cartItem->rowId);
}
Yard::instance('shopping')->reCalculateShippingPrice();
$this->checkCompProduct(Yard::instance('shopping')->getNumComp());
$html_card = view("user.order.yard_view_form", $data)->render();
$html_comp = view("user.order.comp_product", $data)->render();
return response()->json(['response' => true, 'data'=>$data, 'html_card'=>$html_card, 'html_comp'=>$html_comp]);
}
}
if($data['action'] === 'clearCart') {
Yard::instance('shopping')->destroy();
return response()->json(['response' => true, 'data'=>Yard::instance('shopping')->count(), 'html_card'=>'', 'html_comp'=>'']);
}
if($data['action'] === 'updateShippingCountry') {
if(isset($data['shipping_country_id'])){
if($shipping_country = ShippingCountry::find($data['shipping_country_id'])){
Yard::instance('shopping')->setShippingCountryWithPrice($shipping_country->id, $is_for);
$this->checkCompProduct(Yard::instance('shopping')->getNumComp());
}
}
$html_card = view("user.order.yard_view_form", $data)->render();
$html_comp = view("user.order.comp_product", $data)->render();
return response()->json(['response' => true, 'data'=>$data, 'html_card'=>$html_card, 'html_comp'=>$html_comp]);
}
if($data['action'] === 'updateCompProduct'){
// $data['comp_product_id']
// $data['comp_num']
//count_comp_products
$this->updateCompProduct($data);
Yard::instance('shopping')->reCalculateShippingPrice();
$html_card = view("user.order.yard_view_form", $data)->render();
$html_comp = view("user.order.comp_product", $data)->render();
return response()->json(['response' => true, 'data'=>$data, 'html_card'=>$html_card, 'html_comp'=>$html_comp]);
}
return response()->json(['response' => false, 'data'=>$data]);
}
}
private function checkCompProduct($count_comp_products){
foreach (Yard::instance('shopping')->content() as $row) {
//wenn gleich löschen, da neue Versandkosten
if($row->options->comp > $count_comp_products) {
Yard::instance('shopping')->remove($row->rowId);
}
}
}
private function updateCompProduct($data){
//clear old
foreach (Yard::instance('shopping')->content() as $row) {
//wenn kleiner wurde ein produkt entfernt aufgrund der Anzahl
//wenn gleich löschen, da neue Versandkosten
if($row->options->comp === $data['comp_num'] || $row->options->comp > $data['count_comp_products']) {
Yard::instance('shopping')->remove($row->rowId);
}
}
if(isset($data['comp_product_id'])) {
if ($product = Product::find($data['comp_product_id'])) {
$image = "";
if ($product->images->count()) {
$image = $product->images->first()->slug;
}
$cartItem = Yard::instance('shopping')->add($product->id, $product->getLang('name'), 1, 0, false, false,
['image' => $image, 'slug' => $product->slug, 'weight' => 0, 'points' => 0,
'comp' => $data['comp_num'], 'product_id' => $product->id]);
Yard::setTax($cartItem->rowId, 0);
}
}
}
public function customPayment($identifier){
$data = OrderPaymentService::getCustomPayment($identifier);
return view('user.order.payment.custom_payment', $data);
}
private static function customPaymentSendMail($user, $identifier, $yard_shopping_items, $data){
$bcc = [];
$shopping_instance = ShoppingInstance::where('identifier', $identifier)->first();
if(!$shopping_instance){
abort(403, __('msg.shopping_instance_not_found'));
}
$shopping_user = $data['shopping_user_id'] ? ShoppingUser::find($data['shopping_user_id']) : null;
if(!$shopping_user){
abort(403, __('msg.shopping_user_not_found'));
}
$route = route('checkout.checkout_card', ['identifier'=>$identifier]);
$billing_email = $shopping_user->billing_email;
if(!$billing_email){
$billing_email = $data['mode'] === 'test' ? config('app.checkout_test_mail') : config('app.checkout_mail');
}
$bcc[] = $data['mode'] === 'test' ? config('app.checkout_test_mail') : config('app.checkout_mail');
$bcc[] = $shopping_user->member ? $shopping_user->member->email : $user->email;
Mail::to($billing_email)->bcc($bcc)->locale(\App::getLocale())
->send(new MailCustomPaymet($route, $shopping_user, $shopping_instance, $yard_shopping_items, $data['mode']));
}
}

View file

@ -3,28 +3,549 @@
namespace App\Http\Controllers\User;
use Auth;
use Request;
use App\User;
use App\Services\HTMLHelper;
use App\Exports\UserTeamExport;
use App\Models\UserSalesVolume;
use App\Http\Controllers\Controller;
use Maatwebsite\Excel\Facades\Excel;
use App\Models\UserBusiness;
use App\Models\UserLevel;
use App\Models\UserSalesVolume;
use App\Services\BusinessPlan\ExportBot;
use App\Services\BusinessPlan\TreeCalcBot;
use App\Services\BusinessPlan\TreeCalcBotOptimized;
use App\Services\BusinessPlan\TreeHelperOptimized;
use App\Services\HTMLHelper;
use App\Services\NextLevelBadgeHelper;
use App\Services\TranslationHelper;
use App\User;
use Auth;
use Carbon\Carbon;
use function Ramsey\Uuid\v1;
use Maatwebsite\Excel\Facades\Excel;
use Request;
/**
* Team Controller für User-Bereich
*
* Erweitert um optimierte Versionen:
* - show(): Optimierte Team-Übersicht mit Performance-Monitoring
* - structure(): Nutzt TreeCalcBotOptimized für bessere Performance
* - Robuste Fehlerbehandlung mit Fallback zur Standard-Implementierung
* - Memory- und Performance-Monitoring
*/
class TeamController extends Controller
{
private $filter_active = [1 => '', 2 => '', 3 => '']; // Wird in getFilterActive() übersetzt
private $filter_next_level = [0 => '', 1 => '', 2 => '', 3 => '']; // Wird in getFilterNextLevel() übersetzt
private $month;
private $year;
private $forceLiveCalculation;
public function __construct()
{
$this->middleware('active.account');
}
public function members()
/**
* Zeigt die Team-Übersicht mit optimierter TreeCalcBotOptimized-Datenverarbeitung
* Lädt Team-Daten für DataTable-Anzeige
*/
public function show()
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
try {
$this->setFilterVars();
$user = User::find(\Auth::user()->id);
$this->month = session('team_user_filter_month');
$this->year = session('team_user_filter_year');
// Prüfe ob Live-Berechnung erzwungen werden soll
$forceLiveCalculation = Request::get('force_live_calculation', false) || Request::get('live', false);
\Log::info("TeamController: Building optimized team overview for user {$user->id} ({$this->month}/{$this->year})" .
($forceLiveCalculation ? " with forced live calculation" : ""));
// Verwende TreeCalcBotOptimized für bessere Performance
$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'member', $forceLiveCalculation);
$TreeCalcBot->initStructureUser($user->id);
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = round(($endTime - $startTime) * 1000, 2);
$memoryUsed = $this->formatBytes($endMemory - $startMemory);
$calculationType = $forceLiveCalculation ? " (LIVE)" : " (CACHE)";
\Log::info("TeamController: Optimized team overview built in {$executionTime}ms, Memory: {$memoryUsed}{$calculationType}");
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'filter_active' => $this->getFilterActive(),
'filter_levels' => $this->getFilterLevels(),
'filter_next_level' => $this->getFilterNextLevel(),
'TreeCalcBot' => $TreeCalcBot,
'performance' => [
'execution_time' => $executionTime,
'memory_used' => $memoryUsed,
'user_id' => $user->id,
'user_count' => $TreeCalcBot->getTotalUserCount(),
'version' => 'Optimized',
'calculation_type' => $forceLiveCalculation ? 'Live' : 'Cache'
],
'optimized' => true,
'forceLiveCalculation' => $forceLiveCalculation,
];
return view('user.team.show', $data);
} catch (\Exception $e) {
\Log::error("TeamController: Error in optimized show for user {$user->id}: " . $e->getMessage());
// Fallback mit minimalen Daten
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'filter_active' => $this->getFilterActive(),
'filter_levels' => $this->getFilterLevels(),
'filter_next_level' => $this->getFilterNextLevel(),
'error' => __('team.error_loading_optimized_overview') . $e->getMessage(),
'performance' => [
'execution_time' => $executionTime,
'memory_used' => 'N/A',
'version' => 'Fallback',
'calculation_type' => 'Error'
],
'optimized' => false,
];
return view('user.team.show', $data);
}
}
public function structure()
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
$user = User::find(\Auth::user()->id);
if(config('app.debug')){
$user = User::find(454);
}
$this->setFilterVars();
// Prüfe ob optimierte Version explizit angefordert wird
$useOptimized = Request::get('use_optimized', true);
// Prüfe ob Live-Berechnung erzwungen werden soll
$forceLiveCalculation = Request::get('force_live_calculation', false) || Request::get('live', false);
try {
if ($useOptimized) {
// Verwende User-spezifische optimierte Version
$TreeCalcBot = new TreeCalcBotOptimized(
session('team_user_filter_month'),
session('team_user_filter_year'),
'member',
$forceLiveCalculation
);
$TreeCalcBot->initStructureUser($user->id, $forceLiveCalculation);
$optimizedUsed = true;
} else {
// Standard TreeCalcBot mit Performance-Monitoring
$TreeCalcBot = new TreeCalcBot(
session('team_user_filter_month'),
session('team_user_filter_year'),
'member'
);
// Standard TreeCalcBot unterstützt forceLiveCalculation nicht
$TreeCalcBot->initStructureUser($user->id);
$optimizedUsed = false;
}
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = round(($endTime - $startTime) * 1000, 2);
$memoryUsed = $this->formatBytes($endMemory - $startMemory);
$versionInfo = ($optimizedUsed ? "OPTIMIZED" : "STANDARD") .
($forceLiveCalculation ? " + LIVE" : " + CACHE");
\Log::info("TeamController: Structure built for user {$user->id} in {$executionTime}ms, Memory: {$memoryUsed} ({$versionInfo})");
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'TreeCalcBot' => $TreeCalcBot,
'performance' => [
'execution_time' => $executionTime,
'memory_used' => $memoryUsed,
'user_count' => $optimizedUsed && method_exists($TreeCalcBot, 'getTotalUserCount')
? $TreeCalcBot->getTotalUserCount()
: '-',
'version' => $optimizedUsed ? 'Optimized' : 'Standard',
'calculation_type' => $forceLiveCalculation ? 'Live' : 'Cache'
],
'optimized' => $optimizedUsed,
'forceLiveCalculation' => $forceLiveCalculation,
];
return view('user.team.structure', $data);
} catch (\Exception $e) {
\Log::error("TeamController: Error in structure for user {$user->id}: " . $e->getMessage());
// Fallback zur Standard-Implementierung
$TreeCalcBot = new TreeCalcBot(session('team_user_filter_month'), session('team_user_filter_year'), 'member');
$TreeCalcBot->initStructureUser($user->id);
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'TreeCalcBot' => $TreeCalcBot,
'error' => 'Fehler aufgetreten, Standard-Version wird verwendet: ' . $e->getMessage(),
'performance' => [
'execution_time' => $executionTime,
'memory_used' => 'N/A',
'user_count' => '-',
'version' => 'Fallback',
'calculation_type' => $forceLiveCalculation ? __('team.live_not_supported_fallback') : __('team.cache')
],
'optimized' => false,
'forceLiveCalculation' => $forceLiveCalculation,
];
return view('user.team.structure', $data);
}
}
public function structureOld()
{
$user = User::find(\Auth::user()->id);
if(config('app.debug')){
$user = User::find(454);
}
$this->setFilterVars();
$TreeCalcBot = new TreeCalcBot(session('team_user_filter_month'), session('team_user_filter_year'), 'member');
$TreeCalcBot->initStructureUser($user->id);
//for testing
//$TreeCalcBot->initUser(56);
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'TreeCalcBot' => $TreeCalcBot,
];
return view('user.team.structure', $data);
}
/**
* Optimierte DataTable für Team-Übersicht mit TreeCalcBotOptimized-Daten
* Nutzt bereits berechnete Business-Daten für bessere Performance
*/
public function datatableOptimized()
{
try {
$startTime = microtime(true);
$this->setFilterVars();
$user = User::find(\Auth::user()->id);
$this->month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
$this->year = Request::get('team_user_filter_year') ?: session('team_user_filter_year');
// Prüfe ob Live-Berechnung erzwungen werden soll
$forceLiveCalculation = Request::get('force_live_calculation', false) || Request::get('live', false);
\Log::info("TeamController: Building optimized datatable for user {$user->id} ({$this->month}/{$this->year})" .
($forceLiveCalculation == true ? " with forced live calculation" : ""));
// Lade TreeCalcBotOptimized-Daten
$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'member', $forceLiveCalculation);
$TreeCalcBot->initStructureUser($user->id, $forceLiveCalculation);
// Extrahiere alle User aus der Struktur
$teamUsers = collect($this->getTeamUsersFromStructure($TreeCalcBot));
// \Log::info("TeamController: TeamUsers: " . $teamUsers->count());
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$this->forceLiveCalculation = $forceLiveCalculation;
\Log::info("TeamController: Optimized datatable data prepared in {$executionTime}ms for " . $teamUsers->count() . " users");
return \DataTables::of($teamUsers)
->addColumn('id', function ($teamUser) {
return '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
data-id="' . $teamUser->user_id . '"
data-action="business-user-detail"
data-back=""
data-modal="modal-xl"
data-init_from="member"
data-live="' . $this->forceLiveCalculation . '"
data-optimized="1"
data-route="' . route('modal_load') . '"><span class="fa fa-calculator"></span></button>';
})
->addColumn('m_account', function ($teamUser) {
return $teamUser->m_account;
})
->addColumn('email', function ($teamUser) {
return e($teamUser->email);
})
->addColumn('first_name', function ($teamUser) {
return e($teamUser->first_name);
})
->addColumn('last_name', function ($teamUser) {
return e($teamUser->last_name);
})
->addColumn('user_level', function ($teamUser) {
return $teamUser->user_level_name ? TranslationHelper::transUserLevelName($teamUser->user_level_name) : '';
})
->addColumn('is_qual_kp', function ($teamUser) {
$user = User::find($teamUser->user_id);
return TreeHelperOptimized::generateQualKPBadgeForUser($user, $this->month, $this->year);
})
->addColumn('sales_volume_KP_points', function ($teamUser) {
return formatNumber($teamUser->sales_volume_points_KP_sum, 0);
})
->addColumn('sales_volume_total', function ($teamUser) {
return formatNumber($teamUser->payline_points_qual_kp, 0);
})
->addColumn('next_level_qualified', function ($teamUser) {
$userBusiness = UserBusiness::where('user_id', $teamUser->user_id)
->where('month', $this->month)
->where('year', $this->year)
->first();
if ($userBusiness) {
return NextLevelBadgeHelper::generateBadgeFromUserBusiness($userBusiness);
}
return NextLevelBadgeHelper::renderNoDataBadge();
})
->addColumn('active_account', function ($teamUser) {
return get_active_badge($teamUser->active_account);
})
->addColumn('payment_account_date', function ($teamUser) {
return $teamUser->active_date ? formatDate($teamUser->active_date) : "-";
})
->rawColumns(['id', 'next_level_qualified', 'active_account', 'is_qual_kp', 'sales_volume_KP_points', 'sales_volume_total'])
->make(true);
} catch (\Exception $e) {
\Log::error("TeamController: Error in optimized datatable: " . $e->getMessage());
// Fallback zur Standard-DataTable
return $this->datatable();
}
}
/**
* Standard DataTable für Team-Übersicht (Fallback-Version)
*/
public function datatable()
{
try {
$user = User::find(\Auth::user()->id);
$query = $this->initTeamSearch($user);
return \DataTables::eloquent($query)
->addColumn('id', function (User $teamUser) {
return '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
data-id="' . $teamUser->id . '"
data-action="team-user-detail"
data-back=""
data-modal="modal-xl"
data-init_from="member"
data-route="' . route('modal_load') . '"><span class="fa fa-eye"></span></button>';
})
->addColumn('m_account', function (User $teamUser) {
return $teamUser->account ? e($teamUser->account->m_account) : '';
})
->addColumn('user_level', function (User $teamUser) {
return $teamUser->user_level ? e($teamUser->user_level->getLang('name')) : '';
})
->addColumn('is_qual_kp', function (User $teamUser) {
if (!$teamUser->user_level) {
return '-';
}
$qualKP = (int) $teamUser->user_level->qual_kp;
$month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
$year = Request::get('team_user_filter_year') ?: session('team_user_filter_year');
$pointsSum = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_points_KP_sum');
$isQual = $pointsSum >= $qualKP;
$badgeClass = $isQual ? 'badge-outline-success' : 'badge-outline-warning-dark';
return '<span class="badge ' . $badgeClass . '"> KU ' . $qualKP . '</span>';
})
->addColumn('sales_volume_KP_points', function (User $teamUser) {
$month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
$year = Request::get('team_user_filter_year') ?: session('team_user_filter_year');
$total = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_points_KP_sum');
$individual = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_KP_points');
$shop = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_points_shop');
return '<div class="no-line-break">' . $total . '</div>' .
'<span class="small no-line-break">E: ' . $individual . ' | S: ' . $shop . '</span>';
})
->addColumn('sales_volume_total', function (User $teamUser) {
$month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
$year = Request::get('team_user_filter_year') ?: session('team_user_filter_year');
$total = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total_sum');
$individual = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total');
$shop = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total_shop');
return '<div class="no-line-break">' . formatNumber($total) . ' &euro;</div>' .
'<span class="small no-line-break">E: ' . formatNumber($individual) . ' | S: ' . formatNumber($shop) . ' &euro;</span>';
})
->addColumn('email', function (User $teamUser) {
return e($teamUser->email);
})
->addColumn('first_name', function (User $teamUser) {
return $teamUser->account ? e($teamUser->account->first_name) : '';
})
->addColumn('last_name', function (User $teamUser) {
return $teamUser->account ? e($teamUser->account->last_name) : '';
})
->addColumn('sponsor', function (User $teamUser) {
if (!$teamUser->user_sponsor) {
return '-';
}
$sponsor = $teamUser->user_sponsor;
$html = '';
if ($sponsor->account) {
$html .= e($sponsor->account->first_name . ' ' . $sponsor->account->last_name);
$html .= '<br><span class="small no-line-break">' . e($sponsor->email);
$html .= ' | ' . e($sponsor->account->m_account);
$html .= '</span>';
}
return $html;
})
->addColumn('active_account', function (User $teamUser) {
return get_active_badge($teamUser->isActiveAccount());
})
->addColumn('payment_account_date', function (User $teamUser) {
return $teamUser->payment_account ? $teamUser->getPaymentAccountDateFormat(false) : "-";
})
->addColumn('next_level_qualified', function (User $teamUser) {
// Verwende bereits berechnete UserBusiness-Daten für bessere Performance
$month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
$year = Request::get('team_user_filter_year') ?: session('team_user_filter_year');
$userBusiness = UserBusiness::where('user_id', $teamUser->id)
->where('month', $month)
->where('year', $year)
->first();
if ($userBusiness) {
return NextLevelBadgeHelper::generateBadgeFromUserBusiness($userBusiness);
}
return NextLevelBadgeHelper::renderNoDataBadge();
})
->filterColumn('m_account', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('account', function($q) use ($keyword) {
$q->where('m_account', 'LIKE', '%' . $keyword . '%');
});
}
})
->filterColumn('first_name', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('account', function($q) use ($keyword) {
$q->where('first_name', 'LIKE', '%' . $keyword . '%');
});
}
})
->filterColumn('last_name', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('account', function($q) use ($keyword) {
$q->where('last_name', 'LIKE', '%' . $keyword . '%');
});
}
})
->filterColumn('email', function ($query, $keyword) {
if ($keyword != "") {
$query->where('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);
} catch (\Exception $e) {
\Log::error("TeamController: Error in userDatatable: " . $e->getMessage());
return response()->json([
'error' => 'Team-Datatable konnte nicht geladen werden: ' . $e->getMessage()
], 500);
}
}
/**
* Zeigt den Marketingplan für User an
* Übersichtliche Darstellung aller Karriere-Level mit wichtigen Informationen
*/
public function marketingplan()
{
$startTime = microtime(true);
try {
$user = User::find(\Auth::user()->id);
$currentLevel = $user->user_level;
// Lade alle aktiven User Level, sortiert nach Position
$userLevels = \App\Models\UserLevel::where('active', true)
->orderBy('pos', 'asc')
->get();
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
\Log::info("TeamController: Marketingplan loaded for user {$user->id} in {$executionTime}ms");
$data = [
'userLevels' => $userLevels,
'currentUser' => $user,
'currentLevel' => $currentLevel,
'performance' => [
'execution_time' => $executionTime
]
];
return view('user.team.marketingplan', $data);
} catch (\Exception $e) {
\Log::error("TeamController: Error loading marketingplan: " . $e->getMessage());
return view('user.team.marketingplan', [
'error' => __('marketingplan.loading_error') . ' ' . $e->getMessage(),
'userLevels' => collect(),
'currentUser' => null,
'currentLevel' => null
]);
}
}
/**
* Link für neue Mitglieder
*/
public function addMember()
{
$user = User::find(\Auth::user()->id);
if ($user->isActiveShop() && $user->shop) {
@ -39,22 +560,38 @@ class TeamController extends Controller
return view('user.team.members', $data);
}
public function structure()
/**
* Initialisiert die Team-Suche für den eingeloggten User
*/
private function initTeamSearch($currentUser)
{
$user = User::find(\Auth::user()->id);
$this->setFilterVars();
$TreeCalcBot = new TreeCalcBot(session('team_user_filter_month'), session('team_user_filter_year'), 'member');
$TreeCalcBot->initStructureUser($user->id);
//for testing
//$TreeCalcBot->initUser(56);
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'TreeCalcBot' => $TreeCalcBot,
];
return view('user.team.structure', $data);
}
// Finde alle Team-Mitglieder des aktuellen Users (direkte und indirekte)
$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);
// Filtere Team-Mitglieder basierend auf Sponsor-Hierarchie
// TODO: Hier müsste die Logik implementiert werden, um nur Team-Mitglieder des aktuellen Users zu finden
// Für jetzt zeigen wir alle aktiven User (kann später spezifiziert werden)
$activeFilter = Request::get('team_user_filter_active') ?: session('team_user_filter_active', 1);
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)
return $query;
}
public function points()
{
$this->setFilterVars();
@ -153,6 +690,15 @@ class TeamController extends Controller
if (!session('team_user_points_filter_year')) {
session(['team_user_points_filter_year' => intval(date('Y'))]);
}
if (!session('team_user_filter_active')) {
session(['team_user_filter_active' => 1]);
}
if (!session('team_user_filter_level')) {
session(['team_user_filter_level' => 0]);
}
if (!session('team_user_filter_next_level')) {
session(['team_user_filter_next_level' => 0]);
}
if (Request::get('team_user_filter_month')) {
session(['team_user_filter_month' => Request::get('team_user_filter_month')]);
@ -167,6 +713,19 @@ class TeamController extends Controller
if (Request::get('team_user_points_filter_year')) {
session(['team_user_points_filter_year' => Request::get('team_user_points_filter_year')]);
}
if (Request::get('team_user_filter_active')) {
session(['team_user_filter_active' => Request::get('team_user_filter_active')]);
}
if (Request::get('team_user_filter_level')) {
session(['team_user_filter_level' => Request::get('team_user_filter_level')]);
}else{
session(['team_user_filter_level' => 0]);
}
if (Request::get('team_user_filter_next_level')) {
session(['team_user_filter_next_level' => Request::get('team_user_filter_next_level')]);
}else{
session(['team_user_filter_next_level' => 0]);
}
}
private function initSearchPoints()
@ -238,4 +797,206 @@ class TeamController extends Controller
$html = view('user.team._points_sum', $data)->render();
return response()->json(['response' => true, 'data' => $data, 'html' => $html]);
}
/**
* Formatiert Bytes in lesbare Einheiten (aus BusinessControllerOptimized übernommen)
*/
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;
}
/**
* Holt übersetzte Filter für Aktiv-Status
*/
private function getFilterActive(): array
{
return [
1 => __('team.filter_active'),
2 => __('team.filter_not_active'),
3 => __('team.filter_all')
];
}
/**
* Holt übersetzte Filter für Next Level Status
*/
private function getFilterNextLevel(): array
{
return [
0 => __('team.all_status'),
1 => __('team.qualified_green'),
2 => __('team.in_progress_yellow'),
3 => __('team.no_level_red')
];
}
// Performance-optimierte Badge-Generierung wurde in NextLevelBadgeHelper ausgelagert
// Alte performance-lastige Methoden wurden entfernt um die Datatable-Performance zu verbessern
/**
* Extrahiert alle User aus TreeCalcBotOptimized-Struktur für DataTable-Anzeige
* Sammelt rekursiv alle User aus der Struktur und macht sie als flache Liste verfügbar
*/
public function getTeamUsersFromStructure(TreeCalcBotOptimized $treeCalcBot): array
{
$allUsers = [];
$deep = 0;
// Debug: Prüfe TreeCalcBot-Inhalt
$businessUsers = $treeCalcBot->getItems();
\Log::info("TeamController: TreeCalcBot items count: " . count($businessUsers));
// Sammle alle Root-User
foreach ($businessUsers as $businessUser) {
\Log::debug("TeamController: Processing businessUser", [
'user_id' => ($businessUser->user_id),
]);
$businessUser->deep = $deep;
$allUsers[] = $businessUser;
$this->collectUserIdsFromBusinessUser($businessUser, $allUsers, $deep+1, false);
}
// Sammle parentless User
if ($treeCalcBot->isParentless()) {
$parentless = $treeCalcBot->__get('parentless');
//\Log::info("TeamController: Found " . count($parentless) . " parentless users");
if (is_array($parentless)) {
foreach ($parentless as $businessUser) {
if ($businessUser) {
$businessUser->deep = 0;
$allUsers[] = $businessUser;
// Sammle rekursiv alle Unter-User
$this->collectUserIdsFromBusinessUser($businessUser, $allUsers, 0, true);
}
}
}
}
\Log::info("TeamController: AllUsers before filtering: " . count($allUsers));
// Filter anwenden
$filteredUsers = $this->applyTeamFiltersToBusinessUsers($allUsers);
\Log::info("TeamController: AllUsers after filtering: " . count($filteredUsers));
return $filteredUsers;
}
/**
* Wendet Team-Filter auf BusinessUser-Objekte an
*/
private function applyTeamFiltersToBusinessUsers($businessUsers): array
{
$activeFilter = Request::get('team_user_filter_active') ?: session('team_user_filter_active', 1);
$levelFilter = Request::get('team_user_filter_level') ?: session('team_user_filter_level', 0);
$nextLevelFilter = Request::get('team_user_filter_next_level') ?: session('team_user_filter_next_level', 0);
\Log::info("TeamController: Applying filters - Active: {$activeFilter}, Level: {$levelFilter}, NextLevel: {$nextLevelFilter}");
// Debug: Zeige verfügbare Eigenschaften des ersten BusinessUsers
if (!empty($businessUsers)) {
$firstUser = $businessUsers[0];
\Log::debug("TeamController: First BusinessUser properties", [
'user_id' => $firstUser->user_id ?? 'not set',
'active_account' => $firstUser->active_account ?? 'not set',
'm_level_id' => $firstUser->m_level_id ?? 'not set',
'next_qual_user_level' => isset($firstUser->next_qual_user_level) ? 'set' : 'not set',
'next_can_user_level' => isset($firstUser->next_can_user_level) ? 'set' : 'not set',
]);
}
$filtered = array_filter($businessUsers, function($businessUser) use ($activeFilter, $levelFilter, $nextLevelFilter) {
// Active Filter anwenden
if ($activeFilter == 1) { // Nur aktive
if (!$businessUser->active_account) {
return false;
}
} elseif ($activeFilter == 2) { // Nur inaktive
if ($businessUser->active_account) {
return false;
}
}
// activeFilter == 3 bedeutet alle (keine Einschränkung)
// Level Filter anwenden
if ($levelFilter && $levelFilter != 0) {
if ($businessUser->m_level_id != $levelFilter) {
return false;
}
}
// Next Level Filter anwenden
if ($nextLevelFilter && $nextLevelFilter != 0) {
$hasNextQual = ($businessUser->next_qual_user_level) && $businessUser->next_qual_user_level !== '[]';
$hasNextCan = ($businessUser->next_can_user_level) && $businessUser->next_can_user_level !== '[]';
switch ($nextLevelFilter) {
case 1: // Qualifiziert (grün) - hat next_qual_user_level
if (!$hasNextQual) {
return false;
}
break;
case 2: // In Arbeit (gelb) - hat next_can_user_level aber kein next_qual_user_level
if ($hasNextQual || !$hasNextCan) {
return false;
}
break;
case 3: // Kein Level (rot) - hat weder next_qual noch next_can
if ($hasNextQual || $hasNextCan) {
return false;
}
break;
}
}
return true; // Alle Filter bestanden
});
// Array-Indizes neu setzen für korrekte DataTable-Verarbeitung
return array_values($filtered);
}
/**
* Hilfsmethode zum rekursiven Sammeln von User-IDs aus BusinessUser-Struktur
*/
private function collectUserIdsFromBusinessUser($businessUser, &$allUsers, $deep, $parentless): void
{
if (isset($businessUser->businessUserItems) && is_array($businessUser->businessUserItems)) {
\Log::debug("TeamController: Collecting from businessUser with " . count($businessUser->businessUserItems) . " sub-items");
foreach ($businessUser->businessUserItems as $subBusinessUser) {
if ($subBusinessUser) {
$subBusinessUser->deep = $deep;
$allUsers[] = $subBusinessUser;
if($subBusinessUser->user_id){
\Log::debug("TeamController: Collected user ID: " . $subBusinessUser->user_id);
}
// Rekursiver Aufruf für weitere Unter-User
$newDeep = $parentless ? 0 : $deep+1;
$this->collectUserIdsFromBusinessUser($subBusinessUser, $allUsers, $newDeep, $parentless);
}
}
}
}
}

View file

@ -3,7 +3,6 @@
namespace App\Http\Controllers;
use App\Http\Controllers\Api\KasController;
use App\Http\Controllers\Api\KasSLLController;
use App\Models\UserShop;
use App\Models\UserShopOnSite;
use App\Repositories\UserRepository;
@ -308,7 +307,7 @@ class UserShopController extends Controller
if(Request::get('shop_submit') == 'check'){
$rules = array(
'user_shop_name' => ' required|alpha_dash|profanity|unique:user_shops,name|min:4|max:20|full_word_check',
'user_shop_name' => ' required|alpha_dash|unique:user_shops,name|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
if(in_array($value, config('profanity.full_word_check'))){
@ -329,7 +328,7 @@ class UserShopController extends Controller
if(Request::get('shop_submit') == 'action') {
$rules = array(
'user_shop_name' => ' required|alpha_dash|profanity|unique:user_shops,name|min:4|max:20|full_word_check',
'user_shop_name' => ' required|alpha_dash|unique:user_shops,name|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
if(in_array($value, config('profanity.full_word_check'))){
@ -418,7 +417,7 @@ class UserShopController extends Controller
public function checkUserShopName(){
$rules = array(
'user_shop_name' => ' required|alpha_dash|profanity|unique:user_shops,name|min:4|max:20|full_word_check',
'user_shop_name' => ' required|alpha_dash|unique:user_shops,name|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
if(in_array($value, config('profanity.full_word_check'))){

View file

@ -11,7 +11,7 @@ use Carbon\Carbon;
use Illuminate\Database\Connection;
use App\Mail\MailActivateUser;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
class UserUpdateEmailController extends Controller
{
@ -152,7 +152,7 @@ class UserUpdateEmailController extends Controller
protected function getToken()
{
return hash_hmac('sha256', str_random(40), config('app.key'));
return hash_hmac('sha256', Str::random(40), config('app.key'));
}
public function createActivation($user, array $data)

View file

@ -8,11 +8,13 @@ use Request;
use App\Services\Shop;
use App\Services\Util;
use App\Models\Product;
use App\Models\ShoppingUser;
use App\Models\ShoppingInstance;
use App\Http\Controllers\Controller;
class CardController extends Controller
{
private $instance = 'webshop';
/**
* Create a new controller instance.
*
@ -33,16 +35,16 @@ class CardController extends Controller
if($product->images->count()){
$image = $product->images->first()->slug;
}
$cartItem = Yard::instance('shopping')
$cartItem = Yard::instance($this->instance)
->add($product->id, $product->getLang('name'), $quantity,
$product->getPriceWith(Yard::instance('shopping')->getUserTaxFree(), false, Yard::instance('shopping')->getUserCountry()), false, false,
$product->getPriceWith(Yard::instance($this->instance)->getUserTaxFree(), false, Yard::instance($this->instance)->getUserCountry()), false, false,
['image' => $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]);
if(Yard::instance('shopping')->getUserTaxFree()){
if(Yard::instance($this->instance)->getUserTaxFree()){
Yard::setTax($cartItem->rowId, 0);
}else{
Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance('shopping')->getUserCountry()));
Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance($this->instance)->getUserCountry()));
}
Yard::instance('shopping')->reCalculateShippingPrice();
Yard::instance($this->instance)->reCalculateShippingPrice();
\Session()->flash('show-card-after-add', true);
}
@ -62,16 +64,16 @@ class CardController extends Controller
$image = $product->images->first()->slug;
}
$quantity = Request::get('quantity') ? Request::get('quantity') : 1;
$cartItem = Yard::instance('shopping')
$cartItem = Yard::instance($this->instance)
->add($product->id, $product->getLang('name'), $quantity,
$product->getPriceWith(Yard::instance('shopping')->getUserTaxFree(), false, Yard::instance('shopping')->getUserCountry()), false, false,
$product->getPriceWith(Yard::instance($this->instance)->getUserTaxFree(), false, Yard::instance($this->instance)->getUserCountry()), false, false,
['image' => $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]);
if(Yard::instance('shopping')->getUserTaxFree()){
if(Yard::instance($this->instance)->getUserTaxFree()){
Yard::setTax($cartItem->rowId, 0);
}else{
Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance('shopping')->getUserCountry()));
Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance($this->instance)->getUserCountry()));
}
Yard::instance('shopping')->reCalculateShippingPrice();
Yard::instance($this->instance)->reCalculateShippingPrice();
\Session()->flash('show-card-after-add', true);
}
@ -83,15 +85,18 @@ class CardController extends Controller
public function showCard(){
if(Request::get('selected_country')){
Yard::instance('shopping')->setShippingCountryWithPrice(Request::get('selected_country'));
Yard::instance($this->instance)->setShippingCountryWithPrice(Request::get('selected_country'));
}else{
Yard::instance('shopping')->reCalculateShippingPrice();
Yard::instance($this->instance)->reCalculateShippingPrice();
}
//show konflikt wenn user eingeloggt ist und country nicht gesetzt ist
$shipping_error = $this->checkShippingError();
$data = [
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange($this->instance),
'yard_instance' => $this->instance,
'shipping_error' => $shipping_error ?? false,
];
return view('web.templates.card', $data);
}
@ -101,8 +106,8 @@ class CardController extends Controller
$data = Request::all();
if(isset($data['quantity'])){
foreach ($data['quantity'] as $rowId => $qty){
Yard::instance('shopping')->update($rowId, $qty);
Yard::instance('shopping')->reCalculateShippingPrice();
Yard::instance($this->instance)->update($rowId, $qty);
Yard::instance($this->instance)->reCalculateShippingPrice();
}
}else{
$this->deleteCard();
@ -111,31 +116,30 @@ class CardController extends Controller
}
public function checkoutServer(){
$user_shop = Util::getUserShop();
do {
$identifier = Util::getToken();
} while( ShoppingInstance::where('identifier', $identifier)->count() );
$data = [];
$data['is_from'] = 'shopping';
$data['user_price_infos'] = Yard::instance('shopping')->getUserPriceInfos();
$data['user_price_infos'] = Yard::instance($this->instance)->getUserPriceInfos();
ShoppingInstance::create([
'identifier' => $identifier,
'user_shop_id' => $user_shop->id,
'payment' => 1, //Customer Shop Payment
'subdomain' => url('/'),
'country_id' => Yard::instance('shopping')->getShippingCountryId(),
'country_id' => Yard::instance($this->instance)->getShippingCountryId(),
'language' => \App::getLocale(),
'shopping_data' => $data,
'back' => url()->previous(),
]);
Yard::instance('shopping')->store($identifier);
Yard::instance($this->instance)->store($identifier);
//add to DB
$path = route('checkout.checkout_card', ['identifier'=>$identifier]);
if(strpos($path, 'https') === false){
@ -151,19 +155,48 @@ class CardController extends Controller
}
public function removeCard($rowId){
Yard::instance('shopping')->remove($rowId);
Yard::instance($this->instance)->remove($rowId);
return back();
}
public function deleteCard(){
$setCode = Shop::getUserShopLang();
$mylangs = Shop::getLangChange();
$setCode = Shop::getUserShopLang(null, $this->instance);
$mylangs = Shop::getLangChange($this->instance);
foreach($mylangs as $code => $country){
if(strtolower($setCode) === strtolower($code)){
Shop::initUserShopLang($country);
Shop::initUserShopLang($country, $this->instance);
return back();
}
}
}
private function checkShippingError(){
$shipping_error = false;
if(\Auth::guard('customers')->check()){
$user = \Auth::guard('customers')->user();
if($user->shopping_user_id){
$shopping_user = ShoppingUser::find($user->shopping_user_id);
if($shopping_user->same_as_billing){
if($shopping_user->billing_country_id != Yard::instance($this->instance)->getUserCountryId()){
$user_country = Yard::instance($this->instance)->getUserCountry();
$user_country_name = $user_country ? $user_country->getLocated() : '';
$billing_country = $shopping_user->billing_country;
$country_name = $billing_country ? $billing_country->getLocated() : '';
$shipping_error = __('website.shipping_error_billing', ['shipping_country' => $user_country_name, 'billing_country' => $country_name]);
}
}else{
if($shopping_user->shipping_country_id != Yard::instance($this->instance)->getUserCountryId()){
$user_country = Yard::instance($this->instance)->getUserCountry();
$user_country_name = $user_country ? $user_country->getLocated() : '';
$shipping_country = $shopping_user->shipping_country;
$country_name = $shipping_country ? $shipping_country->getLocated() : '';
$shipping_error = __('website.shipping_error_delivery', ['shipping_country' => $user_country_name, 'billing_country' => $country_name]);
}
}
}
}
return $shipping_error;
}
}

View file

@ -24,6 +24,7 @@ use Yard;
class CheckoutController extends Controller
{
private $checkoutRepo;
private $instance = 'checkout';
/**
* Create a new controller instance.
@ -42,7 +43,15 @@ class CheckoutController extends Controller
*/
public function checkout()
{
$shopping_data = Yard::instance('shopping')->getYardExtra('shopping_data');
/*
@if(Auth::guard('customers')->check())
<a href="{{ route('portal.logout') }}" class="btn btn-sm btn-default mt-3"><i class="fa fa-power-off"></i> {{ __('navigation.logout') }} </a>
@else
<a href="{{ Util::getMyMivitaPortalUrl() }}" class="btn btn-primary btn-block mt-3 faa-parent animated-hover"><i class="fa fa-sign-in"></i> {{ __('website.to_customer_portal') }} </a>
@endif
@if(Auth::guard('user')->check())
*/
$shopping_data = Yard::instance($this->instance)->getYardExtra('shopping_data');
$is_from = $shopping_data['is_from'] ?? 'shopping';
$is_for = $shopping_data['is_for'] ?? false;
$is_abo = isset($shopping_data['is_abo']) ? (bool) $shopping_data['is_abo'] : false;
@ -53,7 +62,6 @@ class CheckoutController extends Controller
if ($is_for === 'ot-customer' || $is_for === 'abo-ot-customer') {
$is_from = 'shopping';
}
Util::setInstanceStatus(1, true); // link_check
if ($is_abo) {
$instance_status = Util::getInstanceStatus();
@ -61,29 +69,11 @@ class CheckoutController extends Controller
return $this->redirectToIsFinal($instance_status);
}
}
if (Session::has('new_session')) {
$this->checkoutRepo->sessionDestroy();
Session::forget('new_session');
}
if (!$this->checkoutRepo->getSessionPayments('shopping_user_id')) {
if ($shopping_data && $is_from !== 'shopping') {
$shopping_user = $this->checkoutRepo->shoppingUserAuthData($is_from, $is_for, $shopping_data);
$shopping_user->save();
$this->checkoutRepo->putSessionPayments('shopping_user_id', $shopping_user->id);
} elseif ($is_from === 'shopping' && ($is_for !== 'ot-customer' && $is_for !== 'abo-ot-customer')) {
$shopping_user = $this->initializeShoppingUser($is_for, $is_from, $homeparty_id);
} elseif ($is_from === 'shopping' && ($is_for === 'ot-customer' || $is_for === 'abo-ot-customer')) {
$shopping_user = $this->checkoutRepo->makeCustomerShoppingUser($shopping_data);
$shopping_user->is_for = $is_for;
$shopping_user->is_from = $is_from;
$shopping_user->mode = 'prev';
$shopping_user->language = \App::getLocale();
}
} else {
$shopping_user = $this->getExistingShoppingUser();
}
}
$shopping_user = $this->initializeShoppingUserSession($is_from, $is_for, $shopping_data, $homeparty_id);
$this->prepareShoppingUserData($shopping_user);
$payment_methods = $this->checkoutRepo->getPaymentsMethods($is_from, $is_abo);
@ -101,46 +91,14 @@ class CheckoutController extends Controller
'payment_methods_active' => $payment_methods['active'],
'payment_data' => $payment_methods['data'],
'instance_status' => $instance_status ?? false,
'is_checkout' => true,
'yard_instance' => $this->instance,
];
return view('web.templates.checkout', $data);
}
/**
* Initialisiert einen neuen ShoppingUser
*
* @param string $is_for
* @param string $is_from
* @param int|null $homeparty_id
* @return ShoppingUser
*/
private function initializeShoppingUser($is_for, $is_from, $homeparty_id = null)
{
$shopping_user = new ShoppingUser();
$shopping_user->is_for = $is_for;
$shopping_user->is_from = $is_from;
$shopping_user->homeparty_id = $homeparty_id;
$shopping_user->mode = 'prev';
$shopping_user->language = \App::getLocale();
return $shopping_user;
}
/**
* Holt den existierenden ShoppingUser und bereitet ihn vor
*
* @return ShoppingUser
*/
private function getExistingShoppingUser()
{
$shopping_user = ShoppingUser::findOrFail($this->checkoutRepo->getSessionPayments('shopping_user_id'));
$shopping_user->billing_state = Shop::getCountryShippingCountryId($shopping_user->billing_country_id);
$shopping_user->shipping_state = Shop::getCountryShippingCountryId($shopping_user->shipping_country_id);
$shopping_user->same_as_billing = $shopping_user->same_as_billing ? false : true; // reinvert
return $shopping_user;
}
/**
* Bereitet die ShoppingUser-Daten vor
*
@ -152,25 +110,23 @@ class CheckoutController extends Controller
if ($shopping_user->same_as_billing === NULL) {
$shopping_user->same_as_billing = false;
}
if (!$shopping_user->billing_country_id) {
$shopping_user->billing_country_id = Yard::instance('shopping')->getUserCountryId();
$shopping_user->billing_country_id = Yard::instance($this->instance)->getUserCountryId();
// Die Zeile unten entfernen, da die Relation automatisch geladen wird
// $shopping_user->billing_country = Yard::instance('shopping')->getUserCountry();
// $shopping_user->billing_country = Yard::instance($this->instance)->getUserCountry();
}
if (!$shopping_user->shipping_country_id) {
$shopping_user->shipping_country_id = Yard::instance('shopping')->getUserCountryId();
$shopping_user->shipping_country_id = Yard::instance($this->instance)->getUserCountryId();
// Die Zeile unten entfernen, da die Relation automatisch geladen wird
// $shopping_user->shipping_country = Yard::instance('shopping')->getUserCountry();
// $shopping_user->shipping_country = Yard::instance($this->instance)->getUserCountry();
}
if (old('selected_country') && old('selected_country') === 'change') {
Session::forget('_old_input.selected_country');
$shopping_user->billing_state = old('billing_state');
$shopping_user->shipping_state = old('shipping_state');
} else {
$shopping_user->billing_state = Yard::instance('shopping')->getShippingCountryId();
$shopping_user->shipping_state = Yard::instance('shopping')->getShippingCountryId();
$shopping_user->billing_state = Yard::instance($this->instance)->getShippingCountryId();
$shopping_user->shipping_state = Yard::instance($this->instance)->getShippingCountryId();
}
}
@ -228,9 +184,9 @@ class CheckoutController extends Controller
private function handleCountryChange($data)
{
if (!Request::get('same_as_billing')) {
Yard::instance('shopping')->setShippingCountryWithPrice($data['billing_state'], $data['is_for']);
Yard::instance($this->instance)->setShippingCountryWithPrice($data['billing_state'], $data['is_for']);
} else {
Yard::instance('shopping')->setShippingCountryWithPrice($data['shipping_state'], $data['is_for']);
Yard::instance($this->instance)->setShippingCountryWithPrice($data['shipping_state'], $data['is_for']);
}
return back()->withInput(Request::all());
@ -300,7 +256,7 @@ class CheckoutController extends Controller
// Zahlung vorbereiten
$pay = new PayoneController();
$pay->init($shopping_user, $shopping_order);
$amount = Yard::instance('shopping')->totalWithShipping(2, '.', '') * 100;
$amount = Yard::instance($this->instance)->totalWithShipping(2, '.', '') * 100;
$reference = $pay->setPrePayment($payment_method, $amount, 'EUR', $result);
$this->checkoutRepo->putSessionPayments('payment_reference', $reference);
$pay->setPersonalData();
@ -345,7 +301,7 @@ class CheckoutController extends Controller
if (is_null(Request::get('mandate_identification'))) {
$pay = new PayoneController();
$pay->init($shopping_user, $shopping_order);
$amount = Yard::instance('shopping')->totalWithShipping(2, '.', '') * 100;
$amount = Yard::instance($this->instance)->totalWithShipping(2, '.', '') * 100;
$ret['elv'] = $pay->checkBankAccount($data, $amount, 'EUR', $shopping_user);
if ($ret['elv']['status'] === 'ERROR' || $ret['elv']['status'] === 'INVALID') {
@ -388,6 +344,8 @@ class CheckoutController extends Controller
{
$data = [
'user_shop' => Util::getUserShop(),
'is_checkout' => true,
'yard_instance' => $this->instance,
];
return view('web.templates.checkout-is-final', $data);
@ -444,8 +402,8 @@ class CheckoutController extends Controller
*/
private function handleSuccessfulTransaction($ShoppingPayment, $reference)
{
Yard::instance('shopping')->destroy();
$this->checkoutRepo->sessionDestroy();
Yard::instance($this->instance)->destroy();
$this->checkoutRepo->sessionDestroy(true);
Util::setInstanceStatus(3, true); // link_pending
// Abo erstellen, falls nötig
@ -458,6 +416,8 @@ class CheckoutController extends Controller
'user_shop' => Util::getUserShop(),
'order_reference' => $reference,
'pay_trans' => $payt,
'is_checkout' => true,
'yard_instance' => $this->instance,
];
return view('web.templates.checkout-final', $data);
@ -476,9 +436,8 @@ class CheckoutController extends Controller
if ($payt->shopping_payment->reference != $reference) {
abort(404);
}
Yard::instance('shopping')->destroy();
$this->checkoutRepo->sessionDestroy();
Yard::instance($this->instance)->destroy();
$this->checkoutRepo->sessionDestroy(true);
Util::setInstanceStatus(3, true); // link_pending
// Abo erstellen, falls nötig
@ -495,6 +454,8 @@ class CheckoutController extends Controller
'user_shop' => Util::getUserShop(),
'order_reference' => $payt->shopping_payment->reference,
'pay_trans' => $payt,
'is_checkout' => true,
'yard_instance' => $this->instance,
];
return view('web.templates.checkout-final', $data);
@ -549,4 +510,57 @@ class CheckoutController extends Controller
Payment::paymentStatusSendMail($shopping_order, $shopping_payment, $data);
}
}
/**
* Initialisiert oder ruft einen Shopping-Benutzer ab
*
* @param string|null $is_from = shopping | user_order | user_order_ot | user_order_abo | user_order_abo_ot | user_order_ot_customer | user_order_abo_ot_customer
* @param string|null $is_for = me | ot | abo-me | abo-ot | ot-customer | abo-ot-customer
* @param array|null $shopping_data
* @param int|null $homeparty_id
* @return \App\Models\ShoppingUser
*/
private function initializeShoppingUserSession($is_from, $is_for, $shopping_data = null, $homeparty_id = null)
{
//check if shopping_user_id is set - der user ist bereits angelegt
if ($this->checkoutRepo->getSessionPayments('shopping_user_id')) {
return $this->getExistingShoppingUser();
}
//kommt vom Salescenter
if ($shopping_data && $is_from !== 'shopping') {
$shopping_user = $this->checkoutRepo->shoppingUserAuthData($is_from, $is_for, $shopping_data);
$shopping_user->save();
$this->checkoutRepo->putSessionPayments('shopping_user_id', $shopping_user->id);
return $shopping_user;
}
//kommt aus dem Salescenter mit bestelllink oder aus dem Webshop
if ($is_from === 'shopping') {
//Bestelllink
if ($is_for === 'ot-customer' || $is_for === 'abo-ot-customer') {
//customer shop mit den Daten aus dem Salescenter shopping_data
return $this->checkoutRepo->makeCustomerShoppingUser($shopping_data, $is_for, $is_from);
}
//Webshop
return $this->checkoutRepo->initShoppingUser($is_for, $is_from, $homeparty_id);
}
return $this->getExistingShoppingUser();
}
/**
* Holt den existierenden ShoppingUser und bereitet ihn vor
*
* @return ShoppingUser
*/
private function getExistingShoppingUser()
{
$shopping_user = ShoppingUser::findOrFail($this->checkoutRepo->getSessionPayments('shopping_user_id'));
$shopping_user->billing_state = Shop::getCountryShippingCountryId($shopping_user->billing_country_id);
$shopping_user->shipping_state = Shop::getCountryShippingCountryId($shopping_user->shipping_country_id);
$shopping_user->same_as_billing = $shopping_user->same_as_billing ? false : true; // reinvert
return $shopping_user;
}
}

View file

@ -34,7 +34,8 @@ class ContactController extends Controller
$data = [
'GOOGLE_ReCAPTCHA_KEY' => $this->GOOGLE_ReCAPTCHA_KEY,
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange('webshop'),
'yard_instance' => 'webshop',
];
return view('web.templates.kontakt', $data);
@ -95,7 +96,8 @@ class ContactController extends Controller
$data = [
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange('webshop'),
'yard_instance' => 'webshop',
];
return view('web.templates.contact-final', $data);

View file

@ -42,6 +42,7 @@ class RegisterController extends Controller
$data = [
'GOOGLE_ReCAPTCHA_KEY' => $this->GOOGLE_ReCAPTCHA_KEY,
'user_shop' => Util::getUserShop(),
'yard_instance' => 'webshop',
];
return view('web.templates.registrierung', $data);
}
@ -59,7 +60,8 @@ class RegisterController extends Controller
$data = [
'GOOGLE_ReCAPTCHA_KEY' => $this->GOOGLE_ReCAPTCHA_KEY,
'user_shop' => Util::getUserShop(),
'from_member_id' => $member_id
'from_member_id' => $member_id,
'yard_instance' => 'webshop',
];
return view('web.templates.registrierung', $data);
}
@ -129,6 +131,7 @@ class RegisterController extends Controller
{
$data = [
'user_shop' => Util::getUserShop(),
'yard_instance' => 'webshop',
];
return view('web.templates.registrierung_finish', $data);
}

View file

@ -26,9 +26,11 @@ class SiteController extends Controller
'products' => Product::whereIn('slug', $products)->where('active', true)->whereJsonContains('show_on', '1')->get(),
'set_products' => Product::whereIn('slug', $set_products)->where('active', true)->whereJsonContains('show_on', '1')->get(),
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange('webshop'),
'site' => IqSite::find(1),
'yard_instance' => 'webshop',
];
return view('web.index', $data);
}
@ -39,13 +41,13 @@ class SiteController extends Controller
public function changeLang(){
$data = Request::all();
if(isset($data['change_country_id'])){
$mylangs = Shop::getLangChange();
$mylangs = Shop::getLangChange('webshop');
foreach($mylangs as $code => $country){
if(strtolower($data['change_country_id']) === strtolower($code)){
\Session::put('user_init_country', strtolower($code));
\Session::forget('user_init_country_options');
\Session::put('locale', strtolower($data['change_locale_id']));
Shop::initUserShopLang($country);
Shop::initUserShopLang($country, 'webshop');
return back();
}
}
@ -88,33 +90,33 @@ class SiteController extends Controller
}
//bestelland / versandland
if(array_key_exists($country, Shop::getLangChange())){
if(array_key_exists($country, Shop::getLangChange('webshop'))){
\Session::put('user_init_country_options', $country);
}else{
\Session::put('user_init_country_options', 'de');
}
return redirect(route('home'));
return redirect('/');
}
public function site($site, $subsite = false, $product_slug = false)
{
$this->setIPInfo();
$subsite = trim($subsite, '/');
$product_slug = trim($product_slug, '/');
if($product_slug){
$category = Category::where('slug', $subsite)->where('active', true)->first();
$product = Product::where('slug', $product_slug)->where('active', true)->whereJsonContains('show_on', '1')->first();
if ($category && $product) {
$data = [
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange('webshop'),
'subsite' => $subsite,
'categories' => Category::where('active', true)->orderBy('pos', 'ASC')->get(),
'product' => $product,
'p_count' => Product::where('active', true)->whereJsonContains('show_on', '1')->count(),
'yard_instance' => 'webshop',
];
return view('web.templates.produkte-show', $data);
}
@ -135,7 +137,7 @@ class SiteController extends Controller
$data = [
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange('webshop'),
'subsite' => $subsite,
'categories' => Category::where('active', true)->orderBy('pos', 'DESC')->get(),
'products' => false,
@ -143,6 +145,7 @@ class SiteController extends Controller
'p_count' => Product::where('active', true)->whereJsonContains('show_on', '1')->count(),
'headline' => $category->getLang('headline'),
'headline_image' => $headline_image,
'yard_instance' => 'webshop',
];
return view('web.templates.' . $site, $data);
@ -150,7 +153,7 @@ class SiteController extends Controller
}
$data = [
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange('webshop'),
'subsite' => 'alle-produkte',
'categories' => Category::where('active', true)->orderBy('pos', 'DESC')->get(),
'products' => Product::where('active', true)->whereJsonContains('show_on', '1')->orderBy('pos', 'DESC')->get(),
@ -158,12 +161,14 @@ class SiteController extends Controller
'p_count' => Product::where('active', true)->whereJsonContains('show_on', '1')->count(),
'headline' => __('website.productworld'),
'headline_image' => false,
'yard_instance' => 'webshop',
];
return view('web.templates.'.$site, $data);
}
$data = [
'user_shop' => Util::getUserShop(),
'mylangs' => Shop::getLangChange(),
'mylangs' => Shop::getLangChange('webshop'),
'yard_instance' => 'webshop',
];
if($subsite){
if(!view()->exists('web.templates.'.$subsite)){

View file

@ -45,7 +45,18 @@ class Kernel extends HttpKernel
'admin' => [
'web',
'auth',
]
\App\Http\Middleware\Admin::class,
],
'superadmin' => [
'web',
'auth',
\App\Http\Middleware\SuperAdmin::class,
],
'sysadmin' => [
'web',
'auth',
\App\Http\Middleware\SysAdmin::class,
],
];
/**
@ -63,7 +74,6 @@ class Kernel extends HttpKernel
'sysadmin' => \App\Http\Middleware\SysAdmin::class,
'active.account' => \App\Http\Middleware\ActiveAccount::class,
'active.shop' => \App\Http\Middleware\ActiveShop::class,
'subdomain' => \App\Http\Middleware\Subdomain::class,
'checkout' => \App\Http\Middleware\Checkout::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,

View file

@ -16,11 +16,22 @@ class Admin
*/
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->isAdmin() )
{
if (!Auth::check()) {
return redirect('/home');
}
$user = Auth::user();
// Explizit VIPs blockieren (admin = 1)
if ($user->admin == 1) {
abort(403, 'VIP-Benutzer haben keinen Zugang zum Admin-Bereich.');
}
// Nur echte Admins (admin >= 2) durchlassen
if ($user->admin >= 2) {
return $next($request);
}
return redirect('/home');
}
}

View file

@ -22,20 +22,24 @@ class Checkout
*/
public function handle($request, Closure $next)
{
\Log::debug('Checkout Middleware: ausgeführt', [
'url' => $request->url(),
'host' => $request->getHost()
]);
$instance = 'checkout';
if($shopping_instance = ShoppingInstance::where('identifier', $request->route('identifier'))->first()){
//user shop
//set Lang
\Session::put('locale', $shopping_instance->getLocale());
\App::setLocale($shopping_instance->getLocale());
$user_shop = $shopping_instance->user_shop;
if($user_shop && $user_shop->active == 1 && $user_shop->user->isActiveShop()){
Util::setPostRoute('user/');
\Session::put('user_shop', $user_shop);
\Session::put('user_shop_domain', $shopping_instance->subdomain);
\Session::put('user_shop_payment', $shopping_instance->payment);
\Session::put('user_shop_identifier', $shopping_instance->identifier);
\Session::put('isCheckout', true);
if($shopping_instance->auth_user_id){
\Session::put('auth_user', $shopping_instance->auth_user);
@ -45,21 +49,23 @@ class Checkout
\Session::put('back_link', $shopping_instance->back);
}
\Session::put('new_session', true);
Yard::instance('shopping')->destroy();
Yard::instance($instance)->destroy();
//restore yard
if($shopping_instance->payment !== 6){
Yard::instance('shopping')->restore($request->route('identifier'));
Yard::instance($instance)->restore($request->route('identifier'), [], true, $instance);
}else{
//dont delete shopping instance
Yard::instance('shopping')->restore($request->route('identifier'), [], false);
Yard::instance($instance)->restore($request->route('identifier'), [], false, $instance);
}
Yard::instance('shopping')->putYardExtra('user_shop_payment', $shopping_instance->payment);
Yard::instance('shopping')->putYardExtra('shopping_data', $shopping_instance->shopping_data);
Yard::instance($instance)->putYardExtra('user_shop_payment', $shopping_instance->payment);
Yard::instance($instance)->putYardExtra('shopping_data', $shopping_instance->shopping_data);
$is_for = isset($shopping_instance->shopping_data['is_for']) ? $shopping_instance->shopping_data['is_for'] : 'ot-member';
Yard::instance('shopping')->setUserPriceInfos($shopping_instance->shopping_data['user_price_infos']);
Yard::instance('shopping')->setShippingCountryWithPrice($shopping_instance->country_id, $is_for);
Yard::instance($instance)->setUserPriceInfos($shopping_instance->shopping_data['user_price_infos']);
Yard::instance($instance)->setShippingCountryWithPrice($shopping_instance->country_id, $is_for);
if($shopping_instance->payment !== 6){
//delete shopping instance is not save for restore, payment link
ShoppingInstance::where('identifier', $request->route('identifier'))->delete();
@ -69,11 +75,11 @@ class Checkout
return $next($request);
}
if(\Session::has('user_shop') && \Session::has('isCheckout') && Yard::instance('shopping')->count()){
// \Session::has('user_shop_identifier')
if(\Session::has('user_shop') && Yard::instance($instance)->count() > 0){
return $next($request);
}
return redirect(config('app.url'));
return redirect(Util::getUserCardBackUrl('/card/show', 'checkout'));
}
}

View file

@ -0,0 +1,146 @@
<?php
namespace App\Http\Middleware;
use Closure;
use App\Services\Util;
use App\Models\UserShop;
use Illuminate\Http\Request;
use App\Domain\DomainContext;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Session;
class DomainResolver
{
/**
* Behandelt eine eingehende Anfrage, um den Domain-Kontext aufzulösen.
*
* Diese Middleware ist schlank gehalten. Die Hauptlogik zur Erstellung
* des DomainContext befindet sich im DomainServiceProvider, um eine
* saubere Trennung der Verantwortlichkeiten zu gewährleisten.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
/** @var DomainContext $context */
$context = app(DomainContext::class);
// Session-Domain je nach Kontext setzen
if ($context->type === 'shop') {
Config::set('session.domain', '.'.config('app.domain').config('app.tld_shop'));
} else {
Config::set('session.domain', '.'.config('app.domain').config('app.tld_care'));
}
// Wenn der DomainServiceProvider die Domain nicht identifizieren konnte,
// leiten wir sicher auf die Hauptdomain um.
if ($context->isUnknown()) {
// Detailliertes Logging für spätere Analyse
\Log::warning('Unknown domain accessed', [
'host' => $request->getHost(),
'subdomain' => $context->subdomain,
'user_agent' => $request->userAgent(),
'ip' => $request->ip(),
'referer' => $request->header('referer'),
'path' => $request->getPathInfo()
]);
// Holt die URL der Hauptdomain vom DomainService und leitet um.
$mainUrl = app(\App\Services\DomainService::class)->buildUrl('main');
return redirect()->away($mainUrl, 301);
}
\Log::debug('DomainResolver: context', [
'context' => $context,
'subdomain' => $context->subdomain
]);
// Für User-Shop-Domains: Validierung und Route-Parameter-Bereinigung
if ($context->isUserShop()) {
// Validiere UserShop-Berechtigung (bereits im DomainServiceProvider geprüft,
// aber zusätzliche Sicherheitsebene)
if (!$context->userShop) {
\Log::warning('UserShop not found', [
'subdomain' => $context->subdomain,
'host' => $context->host
]);
abort(503, 'Shop not available');
}
if (!$context->userShop->active) {
\Log::info('UserShop inactive accessed', [
'shop_id' => $context->userShop->id,
'subdomain' => $context->subdomain
]);
abort(503, 'Shop temporarily unavailable');
}
if (!$context->userShop->user || !$context->userShop->user->isActiveShop()) {
\Log::info('UserShop with expired payment accessed', [
'shop_id' => $context->userShop->id,
'user_id' => $context->userShop->user_id ?? null,
'subdomain' => $context->subdomain
]);
abort(503, 'Shop access denied');
}
// Entferne subdomain Parameter aus der Route
// damit catch-all Routen wie /{site}/{subsite?}/{product_slug?} funktionieren
if ($request->route('subdomain')) {
$request->route()->forgetParameter('subdomain');
}
}
// Richtet den Anwendungskontext für Abwärtskompatibilität ein.
$this->setupLegacyContext($context);
return $next($request);
}
/**
* Stellt die Kompatibilität mit älteren Teilen der Anwendung her,
* die direkt auf Session-Daten oder dynamische Konfigurationen zugreifen.
*
* @param DomainContext $context
*/
private function setupLegacyContext(DomainContext $context): void
{
// TODO: [TECH-DEBT] Diese Methode sollte langfristig entfernt werden.
// Alle Teile der Anwendung sollten den DomainContext direkt verwenden.
if ($context->userShop) {
// Setzt die alten Session-Variablen, die von einigen Views/Controllern erwartet werden.
Session::put('user_shop', $context->userShop);
Session::put('user_shop_domain', $context->host);
\Log::debug('DomainResolver: user_shop gesetzt', ['user_id' => $context->userShop->user_id ?? null]);
// Setzt die app.url zur Laufzeit, um URL-Generierung in alten Teilen zu ermöglichen.
Config::set('app.url', $context->host);
// Kompatibilität mit der Util-Klasse.
Util::setPostRoute('user/');
} else {
if($context->type === 'main'){
Session::forget('user_shop');
Session::forget('user_shop_domain');
Session::save(); // Sofortige Session-Speicherung
\Log::debug('DomainResolver: user_shop entfernt (' . $context->type . ' domain)', ['user_shop' => session('user_shop')]);
Config::set('app.url', $context->host);
}elseif($context->type === 'shop'){
Util::setPostRoute('user/');
$user_shop = UserShop::where('slug', 'aloevera')->first();
Session::put('user_shop', $user_shop);
Session::put('user_shop_domain', $context->host);
Session::save(); // Sofortige Session-Speicherung
\Log::debug('DomainResolver: user_shop hinzugefügt (' . $context->type . ' domain)', ['user_shop' => session('user_shop')]);
Config::set('app.url', $context->host);
}else{
// Für Domains ohne UserShop: Session-Daten sofort löschen
// Session::forget('user_shop');
// Session::put('user_shop_domain', $context->host);
// Session::save(); // Sofortige Session-Speicherung
// \Log::debug('DomainResolver: user_shop_domain hinzugefügt (' . $context->type . ' domain)', ['user_shop' => session('user_shop')]);
Config::set('app.url', $context->host);
}
}
}
}

View file

@ -16,11 +16,17 @@ class SuperAdmin
*/
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->isSuperAdmin() )
{
if (!Auth::check()) {
return redirect('/home');
}
$user = Auth::user();
// Nur SuperAdmins (admin >= 3) durchlassen
if ($user->admin >= 3) {
return $next($request);
}
return redirect('/home');
abort(403, 'Sie benötigen SuperAdmin-Rechte für diesen Bereich.');
}
}

View file

@ -16,11 +16,17 @@ class SysAdmin
*/
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->isSySAdmin() )
{
if (!Auth::check()) {
return redirect('/home');
}
$user = Auth::user();
// Nur SysAdmins (admin >= 4) durchlassen
if ($user->admin >= 4) {
return $next($request);
}
return redirect('/home');
abort(403, 'Sie benötigen SysAdmin-Rechte für diesen Bereich.');
}
}

View file

@ -2,22 +2,28 @@
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array
* @var array|string|null
*/
protected $proxies;
protected $proxies = '*';
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_ALL;
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View file

@ -12,6 +12,6 @@ class VerifyCsrfToken extends Middleware
* @var array
*/
protected $except = [
//
'portal/login/verify', // Temporär für OTP-Login
];
}

View file

@ -1,36 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class ExceptionOccured extends Mailable
{
use Queueable, SerializesModels;
public $content;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct($content)
{
$this->content = $content;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('emails.exception')
->with('content', $this->content);
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Mail;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class MailOTPCustomer extends Mailable
{
use Queueable, SerializesModels;
protected $otp;
protected $email;
public $subject;
public function __construct($otp, $email)
{
$this->otp = $otp;
$this->email = $email;
$this->subject = __('portal.mail_subject');
}
public function build()
{
return $this->view('emails.auth')->with([
'otp' => $this->otp,
'url' => route('portal.login.otp.form', ['email' => $this->email, 'otp' => $this->otp]),
'button' => __('portal.login_button'),
'salutation' => __('portal.mail_hello'),
'copy1line' => __('portal.mail_otp'),
'copy2line' => __('portal.mail_otp_valid'),
'copy3line' => __('portal.mail_otp_not_requested'),
'greetings' => __('portal.mail_greetings'),
'sender' => __('portal.mail_sender'),
]);
}
}

74
app/Models/Customer.php Normal file
View file

@ -0,0 +1,74 @@
<?php
namespace App\Models;
use App\User;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; // Wichtig!
/**
*
*
* @property int $id
* @property string|null $name
* @property string $email
* @property int|null $shopping_user_id
* @property int|null $member_id
* @property int|null $number
* @property string|null $language
* @property string|null $mode
* @property string|null $remember_token
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read User|null $member
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
* @property-read int|null $notifications_count
* @property-read \App\Models\ShoppingUser|null $shoppingUser
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereLanguage($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereMemberId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereMode($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereNumber($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereShoppingUserId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Customer extends Authenticatable // Erbt von Authenticatable
{
use HasFactory, Notifiable;
protected $table = 'customers';
protected $fillable = [
'name',
'email',
'shopping_user_id',
'member_id',
'number',
'language',
'mode',
];
// Wichtig: Laravel Auth benötigt dies, auch wenn kein Passwort verwendet wird
protected $hidden = [
'remember_token',
];
public function shoppingUser()
{
return $this->belongsTo(ShoppingUser::class, 'shopping_user_id');
}
public function member()
{
return $this->belongsTo(User::class, 'member_id');
}
}

View file

@ -51,6 +51,11 @@ class ShippingCountry extends Model
return $this->hasMany('App\Models\ShoppingOrder', 'country_id');
}
public function hasShoppingOrders()
{
return $this->shopping_orders()->exists();
}
public static function getActiveShippingCountries(){
$ret = [];

View file

@ -119,11 +119,9 @@ class ShoppingCollectOrder extends Model
{
$tax_split = $this->tax_split;
$add_tax = round($add_tax, 2);
$tax_split[$tax_rate] = isset($tax_split[$tax_rate]) ? round($tax_split[$tax_rate] += $add_tax, 2) : $add_tax;
foreach((array)$tax_split as $key=>$value){
$tax_split[$key] = number_format($value, 2);
}
$existing_value = isset($tax_split[$tax_rate]) ? $this->parseNumericValue($tax_split[$tax_rate]) : 0;
$tax_split[$tax_rate] = round($existing_value + $add_tax, 2);
$this->tax_split = $tax_split;
}
@ -131,14 +129,43 @@ class ShoppingCollectOrder extends Model
{
$net_split = $this->net_split;
$add_net = round($add_net, 2);
$net_split[$tax_rate] = isset($net_split[$tax_rate]) ? round($net_split[$tax_rate] += $add_net, 2) : $add_net;
foreach($net_split as $key=>$value){
$net_split[$key] = number_format($value, 2);
}
$existing_value = isset($net_split[$tax_rate]) ? $this->parseNumericValue($net_split[$tax_rate]) : 0;
$net_split[$tax_rate] = round($existing_value + $add_net, 2);
$this->net_split = $net_split;
}
/**
* Parst verschiedene Zahlenformate zu einem float-Wert
* Unterstützt: 19.50, 1234.56, 1.234,56, 1,234.56
*/
private function parseNumericValue($value)
{
// Bereits eine Zahl? Direkt zurückgeben
if (is_numeric($value)) {
return (float)$value;
}
// String zu String konvertieren für weitere Verarbeitung
$value = (string)$value;
// Entferne Leerzeichen
$value = trim($value);
// Prüfe verschiedene Formate
if (preg_match('/^-?\d{1,3}(\.\d{3})*,\d{2}$/', $value)) {
// Deutsches Format: 1.234,56 oder 1.234.567,89
return (float)str_replace(',', '.', str_replace('.', '', $value));
} elseif (preg_match('/^-?\d{1,3}(,\d{3})*\.\d{2}$/', $value)) {
// Amerikanisches Format: 1,234.56 oder 1,234,567.89
return (float)str_replace(',', '', $value);
} else {
// Einfaches Format: 19.50, 123.45, etc.
// Nur Kommas durch Punkte ersetzen
return (float)str_replace(',', '.', $value);
}
}
public function addShopItem($shop_item_id, $shop_item)
{
$numberFields = [

View file

@ -46,6 +46,8 @@ use Illuminate\Database\Eloquent\Model;
* @method static \Illuminate\Database\Eloquent\Builder|ShoppingPayment whereAboInterval($value)
* @method static \Illuminate\Database\Eloquent\Builder|ShoppingPayment whereCarddata($value)
* @method static \Illuminate\Database\Eloquent\Builder|ShoppingPayment whereIsAbo($value)
* @property string|null $identifier
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShoppingPayment whereIdentifier($value)
* @mixin \Eloquent
*/
class ShoppingPayment extends Model
@ -108,6 +110,7 @@ class ShoppingPayment extends Model
return __('payment.purchase_on_account');
}
}
return __('payment.unknown');
}
public function getPaymentAmount(){

View file

@ -317,4 +317,23 @@ class ShoppingUser extends Model
public function getAllOrdersByMember(){
return ShoppingUserService::getAllOrdersByMember($this);
}
public function getDeliveryCountry($get_country = false){
if($this->same_as_billing == 1){
if($this->billing_country_id){
if($get_country){
return $this->billing_country;
}
return $this->billing_country->getLocated();
}
}else{
if($this->shipping_country_id){
if($get_country){
return $this->shipping_country;
}
return $this->shipping_country->getLocated();
}
}
return 'not set';
}
}

View file

@ -86,7 +86,7 @@ class UserLevel extends Model
public function getNextUserLevels(){
//$ret = [0=>'Keinen'];
$ret = UserLevel::where('active', true)->where('id', '!=', $this->id)->orderBy('pos', 'asc')->get()->pluck('name', 'id')->toArray();
return array_add($ret, 0, '-> Keinen Karriere Level');
return [0 => '-> Keinen Karriere Level'] + $ret;
}
public function setPosAttribute($value){

View file

@ -4,6 +4,7 @@ namespace App\Providers;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\URL;
class AppServiceProvider extends ServiceProvider
{
@ -15,6 +16,29 @@ class AppServiceProvider extends ServiceProvider
public function boot()
{
Schema::defaultStringLength(191);
if ($this->app->environment('production')) {
URL::forceScheme('https');
}
// Domain-bewusster View Composer für user_shop
\View::composer('*', function ($view) {
try {
$context = app(\App\Domain\DomainContext::class);
// Für die Main-Domain: user_shop immer auf null setzen
if ($context->type === 'main') {
$view->with('user_shop', null);
} else {
// Für alle anderen Domains: normales Verhalten
$userShop = $context->userShop ?? \App\Services\Util::getUserShop();
$view->with('user_shop', $userShop);
}
} catch (\Exception $e) {
// Fallback bei Fehlern
$view->with('user_shop', \App\Services\Util::getUserShop());
}
});
}
/**

View file

@ -25,7 +25,12 @@ class AuthServiceProvider extends ServiceProvider
public function boot()
{
$this->registerPolicies();
Passport::routes();
// Die neuere Passport-Konfiguration verwendet separate Methoden
// anstelle von Passport::routes()
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
//
}

View file

@ -0,0 +1,85 @@
<?php
namespace App\Providers;
use App\Domain\DomainContext;
use App\Http\Middleware\DomainResolver;
use App\Models\UserShop;
use App\Services\DomainService;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Support\ServiceProvider;
class DomainServiceProvider extends ServiceProvider
{
/**
* Registriert die Domain-Dienste im Service-Container.
*
* @return void
*/
public function register()
{
// 1. DomainService als Singleton registrieren.
// Er wird die Konfiguration `config/domains.php` verwenden.
$this->app->singleton(DomainService::class, function ($app) {
$domainService = new DomainService($app['config']['domains']);
// Validiere Konfiguration in der Development-Umgebung
if (config('app.debug')) {
$configErrors = $domainService->validateConfiguration();
if (!empty($configErrors)) {
\Log::warning('Domain configuration errors detected', ['errors' => $configErrors]);
}
}
return $domainService;
});
// 2. DomainContext als Singleton registrieren.
// Die Logik hier wird nur einmal pro Anfrage ausgeführt, wenn der
// Context das erste Mal benötigt wird (z.B. in der Middleware).
$this->app->singleton(DomainContext::class, function ($app) {
/** @var DomainService $domainService */
$domainService = $app->make(DomainService::class);
$request = $app->make('request');
// Analysiere den Host der aktuellen Anfrage
$domainInfo = $domainService->parseDomain($request->getHost());
\Log::debug('DomainServiceProvider: domainInfo', [
'domainInfo' => $domainInfo,
'host' => $request->getHost()
]);
$userShop = null;
// Wenn es sich um eine User-Shop-Domain handelt, versuche das Shop-Objekt zu finden.
if ($domainInfo['type'] === 'user-shop' && $domainInfo['subdomain']) {
$userShop = $domainService->getUserShop($domainInfo['subdomain']);
// Wenn der Shop ungültig ist, wird der Typ auf 'unknown' gesetzt.
if (!$userShop) {
$domainInfo['type'] = 'unknown';
}
}
// Wenn es sich um die Haupt-Shop-Domain handelt (z.B. mivita.shop),
// lade den konfigurierten Fallback-Shop.
if ($domainInfo['type'] === 'main-shop' && $domainInfo['default_user_shop']) {
$userShop = $domainService->getUserShop($domainInfo['default_user_shop']);
}
return DomainContext::fromArray($domainInfo, $userShop);
});
}
/**
* Führt Aktionen nach der Registrierung aller Provider aus.
*
* @return void
*/
public function boot(Kernel $kernel)
{
// Registriert die neue Middleware global für die 'web' Gruppe.
// Sie wird vor anderen Middleware ausgeführt, um den Kontext frühzeitig zu setzen.
$kernel = $this->app->make(\Illuminate\Contracts\Http\Kernel::class);
$kernel->prependMiddlewareToGroup('web', DomainResolver::class);
}
}

View file

@ -2,19 +2,28 @@
namespace App\Providers;
use Illuminate\Support\Facades\Route;
use App\Domain\DomainContext;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* This namespace is applied to your controller routes.
*
* In addition, it is set as the URL generator's root namespace.
* The path to the "home" route for your application.
*
* @var string
*/
protected $namespace = 'App\Http\Controllers';
public const HOME = '/';
/**
* The controller namespace for the application.
*
* @var string|null
*/
protected $namespace = 'App\\Http\\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
@ -23,57 +32,97 @@ class RouteServiceProvider extends ServiceProvider
*/
public function boot()
{
parent::boot();
// $this->configureRateLimiting();
$this->routes(function () {
// API-Routen werden global geladen
Route::domain('api.' . config('app.domain') . config('app.tld_care'))
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
// Web-Routen werden domain-bewusst geladen
Route::middleware('web')
->namespace($this->namespace)
->group(function() {
$this->loadDomainAwareRoutes();
});
});
}
/**
* Define the routes for the application.
*
* @return void
* Lädt Routen basierend auf dem aktuellen Domain-Kontext.
*/
public function map()
protected function loadDomainAwareRoutes(): void
{
$this->mapApiRoutes();
/** @var DomainContext $context */
$context = app(DomainContext::class);
$this->loadSharedRoutes();
$this->mapWebRoutes();
//
match ($context->type) {
'main' => $this->loadDomainRoutes('main', 'main.php'),
'main-shop' => [
$this->loadDomainRoutes('shop', 'shop.php'),
$this->loadDomainRoutes('portal', 'portal.php'),
],
'user-shop' => [
$this->loadDomainRoutes('user-shop', 'user-shop.php'),
$this->loadDomainRoutes('portal', 'portal.php'),
],
'crm' => $this->loadDomainRoutes('crm', 'crm.php'),
'portal' => $this->loadDomainRoutes('portal', 'portal.php'),
'checkout' => $this->loadDomainRoutes('checkout', 'checkout.php'),
default => $this->loadAllDomainRoutesForCaching(),
};
}
/**
* Lädt eine spezifische Routendatei für eine Domain.
*/
protected function loadDomainRoutes(string $domainType, string $fileName): void
{
$domain = config("domains.domains.{$domainType}.host");
Route::domain($domain)
->group(base_path('routes/domains/' . $fileName));
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
* Lädt alle domainspezifischen Routen.
* Wird als Fallback für das Route-Caching benötigt.
*/
protected function mapWebRoutes()
protected function loadAllDomainRoutesForCaching(): void
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
if (app()->routesAreCached()) {
return;
}
$this->loadDomainRoutes('main', 'main.php');
$this->loadDomainRoutes('shop', 'shop.php');
$this->loadDomainRoutes('crm', 'crm.php');
$this->loadDomainRoutes('portal', 'portal.php');
$this->loadDomainRoutes('checkout', 'checkout.php');
$this->loadDomainRoutes('user-shop', 'user-shop.php');
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
* Lädt Routen, die auf allen Domains verfügbar sein sollen.
*/
protected function loadSharedRoutes(): void
{
// Lädt Routen, die auf allen Domains verfügbar sein sollen.
Route::group([], base_path('routes/shared/common.php'));
}
/**
* Konfiguriert die Rate-Limiter für die Anwendung.
*
* @return void
*/
protected function mapApiRoutes()
protected function configureRateLimiting()
{
Route::domain('api.'.config('app.domain').config('app.tld_care'))
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
//.
/* Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
*/
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});
}
}

View file

@ -23,7 +23,7 @@ class CheckoutRepository extends BaseRepository {
public function __construct(SessionManager $session)
{
$this->session = $session;
$this->instance = sprintf('%s.%s', 'cart', 'payments');
$this->instance = 'checkout';
}
public function makeShoppingOrder($shopping_user, $data){
@ -34,11 +34,11 @@ class CheckoutRepository extends BaseRepository {
//get data
$homeparty = Homeparty::find($shopping_user->homeparty_id);
//set Data!
$total = Yard::instance('shopping')->total(2, '.', ''); //ek_price
$total = Yard::instance($this->instance)->total(2, '.', ''); //ek_price
$data = [
'shopping_user_id' => $shopping_user->id,
'auth_user_id' => $shopping_user->auth_user_id,
'country_id' => Yard::instance('shopping')->getShippingCountryId(),
'country_id' => Yard::instance($this->instance)->getShippingCountryId(),
'language' => \App::getLocale(),
'user_shop_id' => $user_shop->id,
'payment_for' => $shopping_user->getOrderPaymentFor(),
@ -49,7 +49,7 @@ class CheckoutRepository extends BaseRepository {
'shipping_net' => $homeparty->order['shipping_price_net'],
'subtotal_ws' => 0,
'tax' => $total - $homeparty->order['ek_price_net'],
'total_shipping' => Yard::instance('shopping')->totalWithShipping(2, '.', ''),
'total_shipping' => Yard::instance($this->instance)->totalWithShipping(2, '.', ''),
'points' => $homeparty->order['points'] - $homeparty->order['bonus_points_diff'],
'weight' => 0,
'txaction' => 'prev',
@ -59,11 +59,11 @@ class CheckoutRepository extends BaseRepository {
//get data
$ShoppingCollectOrder = ShoppingCollectOrder::find($shopping_user->shopping_collect_order_id);
//set Data!
$total = Yard::instance('shopping')->total(2, '.', ''); //ek_price
$total = Yard::instance($this->instance)->total(2, '.', ''); //ek_price
$data = [
'shopping_user_id' => $shopping_user->id,
'auth_user_id' => $shopping_user->auth_user_id,
'country_id' => Yard::instance('shopping')->getShippingCountryId(),
'country_id' => Yard::instance($this->instance)->getShippingCountryId(),
'language' => \App::getLocale(),
'user_shop_id' => $user_shop->id,
'payment_for' => $shopping_user->getOrderPaymentFor(),
@ -74,7 +74,7 @@ class CheckoutRepository extends BaseRepository {
'subtotal_ws' => $ShoppingCollectOrder->price_total_net,
'tax' => $ShoppingCollectOrder->tax_total,
'tax_split' => $ShoppingCollectOrder->tax_split,
'total_shipping' => Yard::instance('shopping')->totalWithShipping(2, '.', ''),
'total_shipping' => Yard::instance($this->instance)->totalWithShipping(2, '.', ''),
'points' => $ShoppingCollectOrder->points,
'weight' => 0,
'txaction' => 'prev',
@ -84,19 +84,19 @@ class CheckoutRepository extends BaseRepository {
$data = [
'shopping_user_id' => $shopping_user->id,
'auth_user_id' => $shopping_user->auth_user_id,
'country_id' => Yard::instance('shopping')->getShippingCountryId(),
'country_id' => Yard::instance($this->instance)->getShippingCountryId(),
'language' => \App::getLocale(),
'user_shop_id' => $user_shop->id,
'payment_for' => $shopping_user->getOrderPaymentFor(),
'total' => Yard::instance('shopping')->total(2, '.', ''),
'subtotal' => Yard::instance('shopping')->subtotal(2, '.', ''),
'shipping' => Yard::instance('shopping')->shipping(2, '.', ','),
'shipping_net' => Yard::instance('shopping')->shippingNet(2, '.', ''),
'subtotal_ws' => Yard::instance('shopping')->subtotalWithShipping(2, '.', ''),
'tax' => Yard::instance('shopping')->taxWithShipping(2, '.', ''),
'total_shipping' => Yard::instance('shopping')->totalWithShipping(2, '.', ''),
'points' => Yard::instance('shopping')->points(),
'weight' => Yard::instance('shopping')->weight(),
'total' => Yard::instance($this->instance)->total(2, '.', ''),
'subtotal' => Yard::instance($this->instance)->subtotal(2, '.', ''),
'shipping' => Yard::instance($this->instance)->shipping(2, '.', ','),
'shipping_net' => Yard::instance($this->instance)->shippingNet(2, '.', ''),
'subtotal_ws' => Yard::instance($this->instance)->subtotalWithShipping(2, '.', ''),
'tax' => Yard::instance($this->instance)->taxWithShipping(2, '.', ''),
'total_shipping' => Yard::instance($this->instance)->totalWithShipping(2, '.', ''),
'points' => Yard::instance($this->instance)->points(),
'weight' => Yard::instance($this->instance)->weight(),
'is_abo' => isset($data['is_abo']) ? $data['is_abo'] : false,
'abo_interval' => isset($data['abo_interval']) ? $data['abo_interval'] : null,
'txaction' => 'prev',
@ -121,11 +121,11 @@ class CheckoutRepository extends BaseRepository {
}
$this->putSessionPayments('shopping_order_id', $shopping_order->id);
$items = Yard::instance('shopping')->getContentByOrder();
$items = Yard::instance($this->instance)->getContentByOrder();
$shopping_order->shopping_order_items()->each(function($model) use ($items, $shopping_order, $shopping_user) {
foreach ($items as $item) {
if ($model->row_id === $item->rowId) {
$price_net = Yard::instance('shopping')->rowPriceNet($item, 2, '.', '');
$price_net = Yard::instance($this->instance)->rowPriceNet($item, 2, '.', '');
$tax = $item->price - $price_net;
$data = [
'shopping_order_id' => $shopping_order->id,
@ -159,7 +159,7 @@ class CheckoutRepository extends BaseRepository {
foreach ($items as $item) {
if (!ShoppingOrderItem::where('shopping_order_id', $shopping_order->id)->where('row_id', $item->rowId)->count()){
$price_net = Yard::instance('shopping')->rowPriceNet($item, 2, '.', '');
$price_net = Yard::instance($this->instance)->rowPriceNet($item, 2, '.', '');
$tax = $item->price - $price_net;
$data = [
@ -251,7 +251,7 @@ class CheckoutRepository extends BaseRepository {
abort(404);
}
public function makeCustomerShoppingUser($shopping_data){
public function makeCustomerShoppingUser($shopping_data, $is_for, $is_from){
// $shopping_user = ShoppingUser::findOrFail($shopping_data['shopping_user_id']);
$shopping_user = new ShoppingUser();
$shopping_user->fill($shopping_data);
@ -261,10 +261,90 @@ class CheckoutRepository extends BaseRepository {
$shopping_user->same_as_billing = $shopping_user->same_as_billing ? false : true; //reinvert
// $shopping_user->id = null;
$shopping_user->accepted_data_checkbox = 1;
$shopping_user->is_for = $is_for;
$shopping_user->is_from = $is_from;
$shopping_user->mode = 'prev';
$shopping_user->language = \App::getLocale();
return $shopping_user;
}
public function initShoppingUser($is_for, $is_from, $homeparty_id = null)
{
$shopping_user = new ShoppingUser();
$shopping_user->homeparty_id = $homeparty_id;
$shopping_user->language = \App::getLocale();
//eingeloggter Kunde
if(\Auth::guard('customers')->check()){
$shopping_user = $this->shoppingUserByAuthCustomer(\Auth::guard('customers')->user());
}
//eingeloggter User Berater
if(\Auth::guard('user')->check()){
$shopping_user = $this->shoppingUserByAuthUser(\Auth::guard('user')->user(), $is_from, $is_for);
}
$shopping_user->mode = 'prev';
$shopping_user->is_for = $is_for;
$shopping_user->is_from = $is_from;
return $shopping_user;
}
public function shoppingUserByAuthCustomer(\App\Models\Customer $user){
//clone shopping user!
if($user->shopping_user_id){
$find_shopping_user = ShoppingUser::find($user->shopping_user_id);
if($find_shopping_user){
$shopping_user = $find_shopping_user->replicate();
$shopping_user->billing_country_id = null;
$shopping_user->shipping_country_id = null;
}
}else{
$shopping_user = new ShoppingUser();
$shopping_user->language = \App::getLocale();
}
return $shopping_user;
}
public function shoppingUserByAuthUser(\App\User $user, $is_from, $is_for){
$shopping_user = new ShoppingUser();
$shopping_user->language = \App::getLocale();
$shopping_user->billing_salutation = $user->account->salutation;
$shopping_user->billing_company = $user->account->company;
$shopping_user->billing_firstname = $user->account->first_name;
$shopping_user->billing_lastname = $user->account->last_name;
$shopping_user->billing_address = $user->account->address;
$shopping_user->billing_address_2 = $user->account->address_2;
$shopping_user->billing_zipcode = $user->account->zipcode;
$shopping_user->billing_city = $user->account->city;
//$shopping_user->billing_country_id = $user->account->country_id;
$shopping_user->billing_phone = $user->account->phone;
$shopping_user->billing_email = $user->email;
$shopping_user->faker_mail = false;
$shopping_user->shipping_email = $user->email;
$shopping_user->accepted_data_checkbox = 1;
//Lieferadresse
$shopping_user->same_as_billing = $user->account->same_as_billing ? false : true;
$shopping_user->shipping_salutation = $user->account->shipping_salutation;
$shopping_user->shipping_company = $user->account->shipping_company;
$shopping_user->shipping_firstname = $user->account->shipping_firstname;
$shopping_user->shipping_lastname = $user->account->shipping_lastname;
$shopping_user->shipping_address = $user->account->shipping_address;
$shopping_user->shipping_address_2 = $user->account->shipping_address_2;
$shopping_user->shipping_zipcode = $user->account->shipping_zipcode;
$shopping_user->shipping_city = $user->account->shipping_city;
//$shopping_user->shipping_country_id = $user->account->shipping_country_id;
$shopping_user->shipping_phone = $user->account->shipping_phone;
return $shopping_user;
}
public function shoppingUserAuthData($is_from, $is_for, $data = []){
$user = Util::getAuthUser();
@ -325,7 +405,7 @@ class CheckoutRepository extends BaseRepository {
$shopping_user->shipping_address_2 = isset($data['shipping_address_2']) ? $data['shipping_address_2'] : '';
$shopping_user->shipping_zipcode = isset($data['shipping_zipcode']) ? $data['shipping_zipcode'] : '';
$shopping_user->shipping_city = isset($data['shipping_city']) ? $data['shipping_city'] : '';
$shopping_user->shipping_country_id = Yard::instance('shopping')->getShippingCountryCountryId();
$shopping_user->shipping_country_id = Yard::instance($this->instance)->getShippingCountryCountryId();
$shopping_user->shipping_phone = isset($data['shipping_phone']) ? $data['shipping_phone'] : '';
}else{
@ -361,8 +441,15 @@ class CheckoutRepository extends BaseRepository {
return false;
}
public function sessionDestroy()
public function sessionDestroy($with_shopping = false)
{
if($with_shopping){
if(session('user_shop_payment') === 1){ //ShoppingInstance payment 1 = webshop
Yard::instance('webshop')->destroy();
}else{
Yard::instance('shopping')->destroy();
}
}
$this->session->remove($this->instance);
}

View file

@ -125,7 +125,7 @@ class ContractPDFRepository extends BaseRepository {
$filename = "MIVITA_Beratervertrag.pdf";
Storage::disk($this->disk)->put($this->dir.$filename, $pdf->Output('S'));
$size = Storage::disk($this->disk)->size($this->dir.$filename);
$mine = Storage::disk($this->disk)->getMimeType($this->dir.$filename);
$mine = Storage::disk($this->disk)->mimeType($this->dir.$filename);
File::create([
'user_id' => $this->model->id,

View file

@ -49,7 +49,7 @@ class CreditRepository extends BaseRepository {
if(!Storage::disk('public')->exists( $dir )){
Storage::disk('public')->makeDirectory($dir); //creates directory
}
$path = Storage::disk('public')->getAdapter()->getPathPrefix();
$path = Storage::disk('public')->path('');
$filename = Credit::makeCreditFilename($credit_number);
@ -321,7 +321,7 @@ class CreditRepository extends BaseRepository {
if(!Storage::disk('public')->exists( $dir )){
Storage::disk('public')->makeDirectory($dir); //creates directory
}
$path = Storage::disk('public')->getAdapter()->getPathPrefix();
$path = Storage::disk('public')->path('');
$filename = Credit::makeCreditDetailFilename($user_credit->full_number);
$pdf = new CreditDetailsPDF('pdf.credit_details');
@ -353,7 +353,7 @@ class CreditRepository extends BaseRepository {
if(!Storage::disk('public')->exists( $dir )){
Storage::disk('public')->makeDirectory($dir); //creates directory
}
$path = Storage::disk('public')->getAdapter()->getPathPrefix();
$path = Storage::disk('public')->path('');
$filename = Credit::makeCreditFilename($credit_number);

View file

@ -7,8 +7,9 @@ use App\Models\DcFile;
use App\Services\Util;
use App\Models\DcFileTag;
use App\Repositories\BaseRepository;
use Intervention\Image\Facades\Image;
use Imagick;
use Intervention\Image\ImageManager;
use Imagick;
use ImagickException;
class FileRepository extends BaseRepository {
@ -88,21 +89,22 @@ class FileRepository extends BaseRepository {
private function processImage(string $path, string $filename): void
{
// ImageManager initialisieren (standardmäßig mit GD)
$manager = ImageManager::gd();
// Thumbnail
$img = Image::make($path);
$img->resize(542, 360, function ($c) {
$c->aspectRatio();
$c->upsize();
});
\Storage::disk('public')->put('dc/thumb/'.basename($filename), (string) $img->encode());
$thumbImage = $manager->read($path);
$thumbImage->scaleDown(width: self::THUMB_WIDTH, height: self::THUMB_HEIGHT);
\Storage::disk('public')->put('dc/thumb/'.basename($filename), (string) $thumbImage->toJpeg());
// Big image
$img = Image::make($path);
$img->resize(1600, 900, function ($c) {
$c->aspectRatio();
$c->upsize();
});
\Storage::disk('public')->put('dc/big/'.basename($filename), (string) $img->encode());
$bigImage = $manager->read($path);
$bigImage->scaleDown(width: self::BIG_WIDTH, height: self::BIG_HEIGHT);
\Storage::disk('public')->put('dc/big/'.basename($filename), (string) $bigImage->toJpeg());
// Ressourcen freigeben nicht mehr nötig in v3
// $thumbImage->destroy();
// $bigImage->destroy();
}
private function processPdf(string $path, string $filename): void

View file

@ -0,0 +1,151 @@
<?php
namespace App\Repositories;
use Request;
use App\Models\DcTag;
use App\Models\DcFile;
use App\Models\DcCategory;
class DcRepository extends BaseRepository {
public function __construct()
{
}
public function makeThumb($id){
$file = DcFile::findOrFail($id);
$file->makeThumb();
}
public function deleteFile($id){
$file = DcFile::findOrFail($id);
$file->delete();
}
public function storeItem($obj, $data)
{
if($obj === 'category' && isset($data['dc_category_name'])){
$category = new DcCategory;
$category->name = $data['dc_category_name'];
$category->pos = 0 ;
$category->save();
\Session()->flash('alert-success', 'Kategorie erstellt');
return redirect(route('admin_downloadcenter_tags'));
}
if($obj === 'tag' && isset($data['dc_tag_name'])){
$data = Request::all();
$tag = new DcTag;
$tag->name = $data['dc_tag_name'];
$tag->pos = 0;
$tag->save();
\Session()->flash('alert-success', 'Tag erstellt');
return redirect(route('admin_downloadcenter_tags'));
}
if($obj === 'structure' && isset($data['nestable'])){
$bool = $this->updateStructure($data);
if(Request::ajax()){
return response()->json([
'success' => $bool,
'redirect' => route('admin_downloadcenter_tags', ['flash' => true])
]);
}
}
if($obj === 'update_ajax' && isset($data['action'])){
$active = $this->updateAjax($data);
if(Request::ajax()){
return response()->json([
'success' => $data['action'],
'active' => $active,
]);
}
}
return true;
}
protected function updateAjax($data){
if($data['action'] == 'update-tag-active' && isset($data['id'])){
$tag = DcTag::findOrFail($data['id']);
$tag->active = $tag->active ? 0 : 1;
$tag->save();
return $tag->active;
}
if($data['action'] == 'update-category-active' && isset($data['id'])){
$category = DcCategory::findOrFail($data['id']);
$category->active = $category->active ? 0 : 1;
$category->save();
return $category->active;
}
return false;
}
protected function updateStructure($data)
{
if(empty($data['nestable']) || !is_array($data['nestable'])){
return false;
}
$tags = DcTag::all();
foreach ($tags as $value) {
$value->category_id = null;
$value->pos = NULL;
$value->save();
}
$this->saveStructureLevel($data['nestable']);
return true;
}
protected function saveStructureLevel($nestable, $deep = 0, $category_id = false){
foreach ($nestable as $key => $value) {
if($value['id'] == 0){
continue;
}
if($deep == 0){
$cat = DcCategory::findOrFail($value['id']);
$cat->pos = $key;
$cat->save();
}
if($deep == 1){
$tag = DcTag::findOrFail($value['id']);
$tag->category_id = $category_id;
$tag->pos = $key;
$tag->save();
}
if(!empty($value['children'])){
$this->saveStructureLevel($value['children'], $deep+1, $value['id']);
}
}
}
public function deleteItem($obj, $id){
if($obj == 'category'){
$this->deleteCategory($id);
}
if($obj == 'tag'){
$this->deleteTag($id);
}
}
public function deleteCategory($id){
$cat = DcCategory::findOrFail($id);
$tags = DcTag::where('category_id', $cat->id)->get();
foreach ($tags as $tag) {
$this->deleteTag($tag->id);
}
$cat->delete();
}
public function deleteTag($id){
$tag = DcTag::findOrFail($id);
$tag->delete();
}
}

View file

@ -15,6 +15,9 @@ use App\Services\BusinessPlan\SalesPointsVolume;
class InvoiceRepository extends BaseRepository {
/** @var \App\Models\ShoppingOrder */
protected $model;
private $invoice_date;
private $invoice_number;
private $filename;
@ -125,7 +128,7 @@ class InvoiceRepository extends BaseRepository {
Storage::disk('public')->makeDirectory($this->delivery_dir); //creates directory
}
$path = Storage::disk('public')->getAdapter()->getPathPrefix();
$path = Storage::disk('public')->path('');
//invoice
$pdf_file = new InvoicePDF('pdf.invoice');

View file

@ -76,14 +76,14 @@ class UserRepository extends BaseRepository {
return $this->model;
}
public function deleteUser(User $user)
public function deleteUser(User $user, $complete = false)
{
$active_sponsor = UserUtil::findNextActiveSponsor($user->id);
if($active_sponsor){
UserUtil::setNewSponsorToChilds($user->id, $active_sponsor->id);
UserUtil::setShoppingUserToNewMember($user->id, $active_sponsor->id);
}
UserUtil::deleteUser($user);
UserUtil::deleteUser($user, $complete);
}
public function reverse_charge_validate($data, $user, $route){

View file

@ -391,6 +391,9 @@ class BusinessUserItem
}
public function __get($property) {
if($this->b_user === null){
return null;
}
if (property_exists($this->b_user, $property)) {
return $this->b_user->$property;
}

View file

@ -0,0 +1,797 @@
<?php
namespace App\Services\BusinessPlan;
use App\User;
use stdClass;
use Carbon\Carbon;
use App\Models\UserLevel;
use App\Models\UserBusiness;
use App\Services\TranslationHelper;
use App\Models\UserBusinessStructure;
use Illuminate\Support\Facades\Log;
/**
* Optimierte Version der BusinessUserItem Klasse
*
* Hauptverbesserungen:
* - makeUserFromModel() für bereits geladene User-Objekte
* - Bessere Error-Behandlung mit Logging
* - Optimierte Datenbankzugriffe durch Relations-Nutzung
* - Input-Validierung und Boundary-Checks
*/
class BusinessUserItemOptimized
{
public $businessUserItems = [];
private $date;
private $b_user;
private $user_level_active_pos;
private $needsQualificationRecalculation = false;
public function __construct($date)
{
$this->date = $date;
$this->businessUserItems = []; // Initialize array
return $this;
}
/**
* Erstellt BusinessUser aus User-ID (Original-Methode für Rückwärtskompatibilität)
*
* @param int $user_id Die User-ID
* @param bool $forceLiveCalculation Erzwingt Live-Berechnung und überspringt gespeicherte Daten
*/
public function makeUser($user_id, bool $forceLiveCalculation = false): void
{
try {
// Prüfe nur nach gespeicherten Daten, wenn keine Live-Berechnung erzwungen wird
if (!$forceLiveCalculation) {
$this->b_user = UserBusiness::where('user_id', $user_id)
->where('month', $this->date->month)
->where('year', $this->date->year)
->first();
if ($this->b_user !== null) {
\Log::debug("BusinessUserItem: Using stored data for user {$user_id} ({$this->date->month}/{$this->date->year})");
// WICHTIG: Auch bei gespeicherten Daten User-Model laden für Grunddaten
$user = User::with(['account', 'user_level'])->find($user_id);
if ($user) {
$this->enrichStoredDataWithUserModel($user);
// Prüfe ob Level-Qualifikationsdaten nachberechnet werden müssen
if ($this->needsQualificationRecalculation) {
\Log::debug("BusinessUserItem: Triggering qualification recalculation for user {$user_id}");
$this->calcQualPP(); // Berechne fehlende Level-Qualifikationsdaten
}
}
return; // Bereits gespeicherte Daten verwenden
}
} else {
\Log::debug("BusinessUserItem: Force live calculation for user {$user_id} ({$this->date->month}/{$this->date->year})");
}
// Lade User mit Relations (weniger effizient als makeUserFromModel)
$user = User::with(['account', 'user_level'])->find($user_id);
if (!$user) {
\Log::warning("BusinessUserItem: User not found: {$user_id}");
return;
}
$this->initializeFromUserModel($user);
// WICHTIG: Bei Live-Berechnung auch Level-Qualifikationsdaten berechnen
// (nicht bei forceLiveCalculation=false, da dort gespeicherte Daten bevorzugt werden)
if ($forceLiveCalculation) {
$this->calcQualPP();
}
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error creating user {$user_id}: " . $e->getMessage());
throw $e;
}
}
/**
* NEUE OPTIMIERTE METHODE: Erstellt BusinessUser aus bereits geladenem User-Objekt
* Konsistent zur ursprünglichen makeUser Logik - prüft explizit nach bereits berechneten Daten
*
* @param User $user Das User-Model
* @param bool $forceLiveCalculation Erzwingt Live-Berechnung und überspringt gespeicherte Daten
*/
public function makeUserFromModel(User $user, bool $forceLiveCalculation = false): void
{
try {
if (!$user || !$user->id) {
throw new \InvalidArgumentException('Invalid user model provided');
}
// Prüfe nur nach gespeicherten Daten, wenn keine Live-Berechnung erzwungen wird
if (!$forceLiveCalculation) {
$this->b_user = UserBusiness::where('user_id', $user->id)
->where('month', $this->date->month)
->where('year', $this->date->year)
->first();
if ($this->b_user !== null) {
\Log::debug("BusinessUserItem: Using stored data for user {$user->id} ({$this->date->month}/{$this->date->year})");
// WICHTIG: Auch bei gespeicherten Daten User-Grunddaten anreichern
$this->enrichStoredDataWithUserModel($user);
// Prüfe ob Level-Qualifikationsdaten nachberechnet werden müssen
if ($this->needsQualificationRecalculation) {
\Log::debug("BusinessUserItem: Triggering qualification recalculation for user {$user->id}");
$this->calcQualPP(); // Berechne fehlende Level-Qualifikationsdaten
}
return; // Bereits berechnete Daten verwenden
}
} else {
\Log::debug("BusinessUserItem: Force live calculation for user {$user->id} ({$this->date->month}/{$this->date->year})");
}
// Erstelle neuen User und führe Live-Berechnung durch
$this->initializeFromUserModel($user);
// WICHTIG: Bei Live-Berechnung auch Level-Qualifikationsdaten berechnen
// (nicht bei forceLiveCalculation=false, da dort gespeicherte Daten bevorzugt werden)
if ($forceLiveCalculation) {
$this->calcQualPP();
}
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error creating user from model {$user->id}: " . $e->getMessage());
throw $e;
}
}
/**
* Initialisiert BusinessUser aus User-Model (gemeinsame Logik)
*/
private function initializeFromUserModel(User $user): void
{
// Nutze geladene Relations wenn verfügbar
$user_level_active = null;
if ($user->relationLoaded('user_level')) {
$user_level_active = $user->user_level;
} else {
$user_level_active = $user->user_level; // Fallback auf Original-Relation
}
$this->user_level_active_pos = $user_level_active ? $user_level_active->pos : 0;
// Neues UserBusiness Objekt erstellen
$this->b_user = new UserBusiness();
// Account-Daten (mit Error-Handling)
$account = $user->relationLoaded('account') ? $user->account : null;
if (!$account) {
\Log::warning("BusinessUserItem: No account found for user {$user->id}");
}
$fill = [
'user_id' => $user->id,
'month' => $this->date->month,
'year' => $this->date->year,
'm_level_id' => $user->m_level,
'user_level_name' => $user_level_active ? $user_level_active->name : '',
'active_account' => $this->calculateActiveAccount($user),
'payment_account_date' => $user->payment_account ? $user->getPaymentAccountDateFormat(false) : null,
'active_date' => $user->active_date,
// Account-Daten mit Fallback
'm_account' => $account ? $account->m_account : '',
'email' => $user->email,
'first_name' => $account ? $account->first_name : '',
'last_name' => $account ? $account->last_name : '',
'user_birthday' => $account ? $account->birthday : null,
'user_phone' => $account ? ($account->getPhoneNumber() ?? '') : '',
// Sales Volume (mit Caching falls möglich)
'sales_volume_KP_points' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_KP_points'),
'sales_volume_TP_points' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_TP_points'),
'sales_volume_points_shop' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_points_shop'),
'sales_volume_points_KP_sum' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_points_KP_sum'),
'sales_volume_points_TP_sum' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_points_TP_sum'),
'sales_volume_total' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_total'),
'sales_volume_total_shop' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_total_shop'),
'sales_volume_total_sum' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_total_sum'),
// Level-Daten mit Boundary-Checks
'margin' => $user_level_active ? max(0, $user_level_active->margin) : 0,
'margin_shop' => $user_level_active ? max(0, $user_level_active->margin_shop) : 0,
'qual_kp' => $user_level_active ? max(0, $user_level_active->qual_kp) : 0,
'qual_pp' => $user_level_active ? max(0, $user_level_active->qual_pp) : 0,
// Initialisierung
'payline_points' => 0,
'commission_pp_total' => 0,
'commission_shop_sales' => 0,
'commission_growth_total' => 0,
'version' => 2,
];
$this->b_user->fill($fill);
$this->b_user->business_lines = [];
$this->b_user->user_items = [];
// Shop-Provision berechnen (mit Boundary-Check)
$shopVolume = (float) $this->b_user->sales_volume_total_shop;
$shopMargin = (float) $this->b_user->margin_shop;
$this->b_user->commission_shop_sales = round($shopVolume / 100 * $shopMargin, 2);
\Log::debug("BusinessUserItem: Created optimized user {$user->id} for {$this->date->month}/{$this->date->year}");
}
/**
* Ergänzt gespeicherte UserBusiness-Daten mit aktuellen User-Grunddaten
* Erweitert um Level-Qualifikationsdaten-Validierung für Struktur-Ansicht
*/
private function enrichStoredDataWithUserModel(User $user): void
{
try {
$account = $user->account;
// Ergänze fehlende User-Grunddaten in gespeicherten UserBusiness-Daten
$this->b_user->user_id = $user->id;
$this->b_user->email = $user->email;
$this->b_user->first_name = $account ? $account->first_name : '';
$this->b_user->last_name = $account ? $account->last_name : '';
$this->b_user->user_birthday = $account ? $account->birthday : null;
$this->b_user->user_phone = $account ? ($account->getPhoneNumber() ?? '') : '';
$this->b_user->m_account = $account ? $account->m_account : '';
// Berechne aktiven Account-Status
$this->b_user->active_account = $this->calculateActiveAccount($user);
$this->b_user->payment_account_date = $user->payment_account;
// User-Level Informationen
$user_level_active = $user->user_level;
if ($user_level_active) {
$this->b_user->user_level_name = $user_level_active->name;
$this->user_level_active_pos = $user_level_active->pos;
}
// WICHTIG: Validiere Level-Qualifikationsdaten für Struktur-Ansicht
$this->validateLevelQualificationData();
\Log::debug("BusinessUserItem: Enriched stored data for user {$user->id} with current user model data");
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error enriching stored data for user {$user->id}: " . $e->getMessage());
}
}
/**
* Validiert und aktualisiert Level-Qualifikationsdaten wenn nötig
* Stellt sicher, dass next_qual_user_level und next_can_user_level für Struktur-Ansicht verfügbar sind
*/
private function validateLevelQualificationData(): void
{
try {
// Prüfe ob Level-Qualifikationsdaten vorhanden sind
$hasNextQual = !empty($this->b_user->next_qual_user_level);
$hasNextCan = !empty($this->b_user->next_can_user_level);
$hasQualUserLevel = !empty($this->b_user->qual_user_level);
// Wenn Level-Qualifikationsdaten fehlen, führe Neuberechnung durch
if (!$hasNextQual && !$hasNextCan && !$hasQualUserLevel) {
\Log::debug("BusinessUserItem: Level qualification data missing for user {$this->b_user->user_id}, triggering recalculation");
// Setze Flag für notwendige Neuberechnung
$this->needsQualificationRecalculation = true;
}
} catch (\Exception $e) {
\Log::warning("BusinessUserItem: Error validating level qualification data for user {$this->b_user->user_id}: " . $e->getMessage());
}
}
/**
* Berechnet ob Account aktiv ist (mit Error-Handling)
*/
private function calculateActiveAccount(User $user): bool
{
try {
if (!$user->payment_account) {
return false;
}
// Verwende aktuelles Datum, nicht das Berechnungs-Startdatum
return Carbon::parse($user->payment_account)->gt(Carbon::now());
} catch (\Exception $e) {
\Log::warning("BusinessUserItem: Error calculating active account for user {$user->id}: " . $e->getMessage());
return false;
}
}
/**
* Optimierte Sales Volume Abfrage (mit potenziellem Caching)
*/
private function getUserSalesVolumeOptimized(User $user, string $field)
{
try {
// Hier könnte Caching implementiert werden
$cacheKey = "sales_volume_{$user->id}_{$this->date->month}_{$this->date->year}_{$field}";
// Für jetzt: Direkter Aufruf (später durch Cache ersetzen)
return $user->getUserSalesVolumeBy($this->date->month, $this->date->year, $field);
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error getting sales volume {$field} for user {$user->id}: " . $e->getMessage());
return 0; // Sicherer Fallback
}
}
// ===== ORIGINALE METHODEN (unverändert für Kompatibilität) =====
public function getSalesVolumeTotalMargin()
{
return $this->b_user->getSalesVolumeTotalMargin();
}
public function addUserID()
{
TreeCalcBotOptimized::addUserID($this->b_user->user_id);
}
public function getBUser()
{
return $this->b_user;
}
public function addBusinessLineToUser($line, $obj)
{
$this->b_user->business_lines[$line] = $obj;
}
public function addBusinessLinePoints($line, $points)
{
if (!isset($this->b_user->business_lines[$line])) {
\Log::warning("BusinessUserItem: Trying to add points to non-existent line {$line}");
return;
}
$obj = $this->b_user->business_lines[$line];
// Handle both array and object types (JSON deserialization inconsistency)
if (is_array($obj)) {
$obj['points'] = ($obj['points'] ?? 0) + (float) $points;
} else {
// Ensure it's an object
if (!is_object($obj)) {
$obj = (object) $obj;
}
$obj->points = ($obj->points ?? 0) + (float) $points;
}
$this->b_user->business_lines[$line] = $obj;
}
public function addTotalTP($points)
{
$this->b_user->total_pp += (float) $points; // Type-Safety
}
public function isQualKP(): bool
{
return ($this->b_user->sales_volume_points_KP_sum >= $this->b_user->qual_kp);
}
public function isQualLevel(): bool
{
return !empty($this->b_user->qual_user_level);
}
public function isQualEqualLevel(): bool
{
if (!$this->b_user->qual_user_level) {
return false;
}
return ($this->b_user->m_level_id == $this->b_user->qual_user_level['id']);
}
public function getQualPaylines(): int
{
if (!$this->b_user->qual_user_level) {
return 0;
}
return (int) $this->b_user->qual_user_level['paylines'];
}
public function getRestQualKP(): float
{
$ret = $this->b_user->sales_volume_points_KP_sum - $this->b_user->qual_kp;
return max(0, $ret); // Boundary-Check
}
public function getCommissionTotal(): float
{
return round(
$this->b_user->commission_shop_sales +
$this->b_user->commission_pp_total +
$this->b_user->commission_growth_total,
2
);
}
// ===== PROVISIONSBERECHNUNG (Original-Logik) =====
public function calcQualPP($force = false): void
{
try {
$qualUserLevel = $this->calcuQualLevel();
if ($qualUserLevel !== null) {
//das erreichte level setzen
$this->b_user->qual_user_level = $qualUserLevel->toArray();
//next_qual_user_level nächster qualifizierten level
$this->setNextUserLevel($force);
//qual_user_level_next nächste Provisions-Stufe,
$this->setQualNextLevel($force);
//provisionen berechnen
$this->calculateCommissions($qualUserLevel);
} else {
$this->setFirstQualLevel();
}
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error calculating qualifications for user {$this->b_user->user_id}: " . $e->getMessage());
}
}
/**
* Berechnet Provisionen mit Error-Handling
* Erweitert um Array/Object-Kompatibilität für business_lines
*/
private function calculateCommissions($qualUserLevel): void
{
$commission_pp_total = 0;
$commission_growth_total = 0;
// Payline-Provisionen
for ($i = 1; $i <= $qualUserLevel->paylines; $i++) {
if (isset($this->b_user->business_lines[$i])) {
$object = $this->b_user->business_lines[$i];
$margin = (float) $this->b_user->qual_user_level['pr_line_'.$i];
// Handle both array and object types (JSON deserialization inconsistency)
if (is_array($object)) {
$points = (float) ($object['points'] ?? 0);
$object['margin'] = $margin;
$object['commission'] = round($points / 100 * $margin, 2);
$object['payline'] = true;
$commission_pp_total += $object['commission'];
} else {
$points = (float) ($object->points ?? 0);
$object->margin = $margin;
$object->commission = round($points / 100 * $margin, 2);
$object->payline = true;
$commission_pp_total += $object->commission;
}
$this->b_user->business_lines[$i] = $object;
}
}
// Growth Bonus
if (!empty($qualUserLevel->growth_bonus)) {
$payline = (int) $this->b_user->qual_user_level['paylines'] + 1;
$maxlines = count($this->b_user->business_lines) + 1;
$growth_bonus = (float) $this->b_user->qual_user_level['growth_bonus'];
for ($i = $payline; $i <= $maxlines; $i++) {
if (isset($this->b_user->business_lines[$i])) {
$object = $this->b_user->business_lines[$i];
// Handle both array and object types (JSON deserialization inconsistency)
if (is_array($object)) {
$points = (float) ($object['points'] ?? 0);
$object['margin'] = $growth_bonus;
$object['commission'] = round($points / 100 * $growth_bonus, 2);
$object['growth_bonus'] = true;
$commission_growth_total += $object['commission'];
} else {
$points = (float) ($object->points ?? 0);
$object->margin = $growth_bonus;
$object->commission = round($points / 100 * $growth_bonus, 2);
$object->growth_bonus = true;
$commission_growth_total += $object->commission;
}
$this->b_user->business_lines[$i] = $object;
}
}
}
$this->b_user->commission_pp_total = $commission_pp_total;
$this->b_user->commission_growth_total = $commission_growth_total;
}
// ===== WEITERE ORIGINAL-METHODEN (gekürzt, vollständige Implementation in Original) =====
//aktuelles level berechnen, max das eigene level, wenn weniger Points dann darunter
public function calcuQualLevel()
{
$qualUserLevels = UserLevel::where('qual_kp', '<=', $this->b_user->sales_volume_points_KP_sum)
->where('pos', '<=', $this->user_level_active_pos)
->orderBy('qual_pp', 'desc')
->get();
foreach ($qualUserLevels as $qualUserLevel) {
$payline_points = $this->getPointsforPayline($qualUserLevel->paylines);
$payline_points_qual_kp = $payline_points + $this->getRestQualKP();
if ($payline_points_qual_kp >= $qualUserLevel->qual_pp) {
$this->b_user->payline_points = $payline_points;
$this->b_user->payline_points_qual_kp = $payline_points_qual_kp;
return $qualUserLevel;
}
}
return null;
}
private function getPointsforPayline($paylines): float
{
$payline_points = 0;
for ($i = 1; $i <= $paylines; $i++) {
if (isset($this->b_user->business_lines[$i])) {
$line = $this->b_user->business_lines[$i];
// Handle both array and object types (JSON deserialization inconsistency)
if (is_array($line)) {
$payline_points += (float) ($line['points'] ?? 0);
} else {
$payline_points += (float) ($line->points ?? 0);
}
}
}
return $payline_points;
}
/**
* Setzt das nächste Provision-Level
* Wenn das aktuelle Level nicht erreicht ist, dann wird bei aktuelle Provisions-Stufe die erreichte level angezeigt und berechnet
* Zur Info wird das nächste level angezeigt, der folgt, sonst leer
*/
private function setQualNextLevel($force = false): void
{
//ist der level nicht das aktuelle level, dann sucht es den nächsten level
//isQualEqualLevel wenn das erreichte level das akutelle user level ist.
if (!$this->isQualEqualLevel() && $this->b_user->qual_user_level['next_id'] != null) {
$qualUserLevelNext = UserLevel::where('id', '=', $this->b_user->qual_user_level['next_id'])
->orderBy('qual_pp', 'asc')
->first();
if ($qualUserLevelNext) {
$this->b_user->qual_user_level_next = $qualUserLevelNext->toArray();
}else{
$this->b_user->qual_user_level_next = null;
}
}else{
$this->b_user->qual_user_level_next = null;
}
}
private function setNextUserLevel($force = false): void
{
//sucht den nächsten level, der mehr points hat als das aktuelle level
$nextQualUserLevel = UserLevel::where('qual_pp', '<=', $this->b_user->payline_points_qual_kp)
->where('pos', '>', $this->user_level_active_pos)
->orderBy('qual_pp', 'desc')
->first();
//wenn der nächste level qualifiziert ist und die KP-Qualifikation erfüllt ist, dann setzt es den nächsten level
if ($nextQualUserLevel && $this->isQualKP()) {
$this->b_user->next_qual_user_level = $nextQualUserLevel->toArray();
$this->b_user->next_can_user_level = null;
} else {
//wenn der nächste level nicht qualifiziert ist, dann sucht es den nächsten level, nach pos
$nextCanUserLevel = UserLevel::where('pos', '>', $this->user_level_active_pos)
->orderBy('qual_pp', 'asc')
->first();
if ($nextCanUserLevel) {
$this->b_user->next_can_user_level = $nextCanUserLevel->toArray();
}
$this->b_user->next_qual_user_level = null;
}
}
private function setFirstQualLevel(): void
{
$qualUserLevelNext = UserLevel::where('pos', '=', 1)
->orderBy('qual_pp', 'asc')
->first();
if ($qualUserLevelNext) {
$this->b_user->qual_user_level_next = $qualUserLevelNext->toArray();
}
}
// Magic Methods für Property-Zugriff (Rückwärtskompatibilität)
public function __get($name)
{
if (isset($this->b_user->$name)) {
return $this->b_user->$name;
}
// Legacy-Properties
$legacyMap = [
'sales_volume_points_KP_sum' => 'sales_volume_points_KP_sum',
'sales_volume_points_TP_sum' => 'sales_volume_points_TP_sum',
'business_lines' => 'business_lines',
'user_id' => 'user_id'
];
if (isset($legacyMap[$name]) && isset($this->b_user->{$legacyMap[$name]})) {
return $this->b_user->{$legacyMap[$name]};
}
return null;
}
/**
* Prüft und setzt Sponsor-Informationen (Original-Implementation)
*/
public function checkSponsor($user): void
{
try {
// Check if already stored
if ($this->isSave()) {
return;
}
$sponsor = new stdClass();
$sponsor->is_sponsor = false;
$sponsor->user_id = false;
$sponsor->first_name = '';
$sponsor->last_name = '';
$sponsor->email = '';
$sponsor->m_account = '';
$sponsor->full_name = 'Keinen Sponsor zugewiesen';
if ($user->m_sponsor) {
if ($user->user_sponsor) {
$sponsor->is_sponsor = true;
$sponsor->user_id = $user->user_sponsor->id;
if ($user->user_sponsor->account) {
$sponsor->full_name = substr(
'Sponsor: ' . $user->user_sponsor->account->first_name . ' ' .
$user->user_sponsor->account->last_name . ' | ' .
$user->user_sponsor->email . ' | ' .
$user->user_sponsor->account->m_account, 0, 250
);
$sponsor->first_name = $user->user_sponsor->account->first_name;
$sponsor->last_name = $user->user_sponsor->account->last_name;
$sponsor->m_account = $user->user_sponsor->account->m_account;
} else {
$sponsor->full_name = 'Sponsor: ' . $user->user_sponsor->email;
}
$sponsor->email = $user->user_sponsor->email;
} else {
$sponsor->full_name = 'Sponsor wurde gelöscht.';
}
}
$this->b_user->sponsor = $sponsor;
} catch (\Exception $e) {
Log::error("BusinessUserItem: Error checking sponsor for user {$user->id}: " . $e->getMessage());
}
}
/**
* Lädt Parent Business Users rekursiv (Original-Implementation mit Optimierungen)
*/
public function readParentsBusinessUsers($forceLiveCalculation = false): void
{
try {
// Optimiert: Lade mit Relations
$users = User::with(['account'])
->select('users.*')
->where('users.deleted_at', '=', null)
->where('users.id', '!=', 1)
->where('users.admin', '<', 4)
->where('users.m_level', '!=', null)
->where('users.m_sponsor', '=', $this->b_user->user_id)
->where('users.payment_account', '!=', null)
->where('users.active_date', '<=', $this->date->end_date)
->get();
if ($users->isNotEmpty()) {
foreach ($users as $user) {
$businessUserItem = new BusinessUserItemOptimized($this->date);
$businessUserItem->makeUserFromModel($user, $forceLiveCalculation);
$businessUserItem->addUserID();
$this->businessUserItems[] = $businessUserItem;
}
}
// Rekursiver Aufruf für alle Child-Items
foreach ($this->businessUserItems as $businessUserItem) {
$businessUserItem->readParentsBusinessUsers($forceLiveCalculation);
}
} catch (\Exception $e) {
Log::error("BusinessUserItem: Error reading parent users for {$this->b_user->user_id}: " . $e->getMessage());
}
}
/**
* Lädt Parent Business Users aus gespeicherter Struktur (Original-Implementation)
*/
public function readStoredParentsBusinessUsers($structure): void
{
try {
$parents = $this->findParentsBusinessOnStored($this->b_user->user_id, $structure);
if ($parents) {
foreach ($parents as $obj) {
$businessUserItem = new BusinessUserItemOptimized($this->date);
$businessUserItem->makeUser($obj->user_id);
$businessUserItem->addUserID();
$this->businessUserItems[] = $businessUserItem;
}
foreach ($this->businessUserItems as $businessUserItem) {
$businessUserItem->readStoredParentsBusinessUsers($parents);
}
}
} catch (\Exception $e) {
Log::error("BusinessUserItem: Error reading stored parent users: " . $e->getMessage());
}
}
/**
* Findet Parent Business Items in gespeicherter Struktur (Original-Implementation)
*/
private function findParentsBusinessOnStored($user_id, $structures)
{
if (!$structures) {
return null;
}
foreach ($structures as $obj) {
if ($user_id === $obj->user_id) {
return $obj->parents ?? null;
}
if (!empty($obj->parents)) {
$result = $this->findParentsBusinessOnStored($user_id, $obj->parents);
if ($result) {
return $result;
}
}
}
return null;
}
/**
* Prüft ob User bereits gespeichert ist
* Konsistent zur ursprünglichen BusinessUserItem Implementation
*/
public function isSave(): bool
{
return $this->b_user && $this->b_user->isSave();
}
/**
* Gibt die Anzahl der qualifizierten Paylines zurück
*/
public function getQualLevelPaylines()
{
if ($this->b_user && isset($this->b_user->qual_user_level) && $this->b_user->qual_user_level) {
return $this->b_user->qual_user_level['paylines'] ?? 0;
}
return 0;
}
/**
* Prüft ob eine Line für Growth-Bonus qualifiziert ist
*/
public function isQualLevelGrowth($line)
{
if ($this->b_user && isset($this->b_user->business_lines[$line])) {
$object = $this->b_user->business_lines[$line];
if (isset($object->growth_bonus)) {
return $object->growth_bonus > 0;
}
}
return false;
}
}

View file

@ -0,0 +1,198 @@
<?php
namespace App\Services\BusinessPlan;
use App\User;
use App\Models\UserBusinessStructure;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\LazyCollection;
use Illuminate\Support\Facades\Log;
/**
* Repository für effiziente Datenbankabfragen im Business-Kontext
* Löst N+1 Probleme durch optimierte Eager Loading Strategien
*/
class BusinessUserRepository
{
private $startDate;
private $endDate;
private $month;
private $year;
public function __construct(int $month, int $year)
{
$this->month = $month;
$this->year = $year;
$date = Carbon::parse($year.'-'.$month.'-1');
$this->startDate = $date->format('Y-m-d H:i:s');
$this->endDate = $date->endOfMonth()->format('Y-m-d H:i:s');
}
/**
* Lädt Root-User mit optimiertem Eager Loading und Caching
*/
public function getRootUsers(): Collection
{
$cacheKey = "root_users_{$this->month}_{$this->year}";
return cache()->remember($cacheKey, 3600, function() {
\Log::info("BusinessUserRepository: Loading root users from database (cache miss)");
return User::with([
'account',
'user_level',
'userBusiness' => function($query) {
$query->where('month', $this->month)
->where('year', $this->year);
}
])
->select('users.*')
->where('users.deleted_at', '=', null)
->where('users.id', '!=', 1)
->where('users.admin', '<', 4)
->where('users.m_level', '!=', null)
->where('users.m_sponsor', '=', null)
->where('users.payment_account', '!=', null)
->where('users.active_date', '<=', $this->endDate)
->get();
});
}
/**
* Lädt User ohne Parent-Zuordnung (Lazy Loading für Memory-Effizienz)
*/
public function getParentlessUsers(array $excludeUserIds = []): LazyCollection
{
$query = User::with([
'account',
'user_level',
'userBusiness' => function($query) {
$query->where('month', $this->month)
->where('year', $this->year);
}
])
->select('users.*')
->where('users.deleted_at', '=', null)
->where('users.id', '!=', 1)
->where('users.admin', '<', 4)
->where('users.payment_account', '!=', null)
->where('users.active_date', '<=', $this->endDate);
if (!empty($excludeUserIds)) {
$query->whereNotIn('users.id', $excludeUserIds);
}
return $query->lazy(100);
}
/**
* Lädt einen einzelnen User mit Relations und Caching
*/
public function getUserWithRelations(int $userId): ?User
{
$cacheKey = "user_relations_{$userId}_{$this->month}_{$this->year}";
return cache()->remember($cacheKey, 1800, function() use ($userId) {
\Log::debug("BusinessUserRepository: Loading user {$userId} with relations (cache miss)");
return User::with([
'account',
'user_level',
'userBusiness' => function($query) {
$query->where('month', $this->month)
->where('year', $this->year);
}
])->find($userId);
});
}
/**
* Lädt Sponsor für einen User
*/
public function getSponsorForUser(int $userId): ?User
{
$user = $this->getUserWithRelations($userId);
if (!$user || !$user->m_sponsor) {
return null;
}
return $this->getUserWithRelations($user->m_sponsor);
}
/**
* Prüft ob gespeicherte Struktur existiert (mit Caching)
*/
public function getStoredStructure(): ?UserBusinessStructure
{
$cacheKey = "stored_structure_{$this->month}_{$this->year}";
return cache()->remember($cacheKey, 7200, function() {
\Log::debug("BusinessUserRepository: Loading stored structure (cache miss)");
$structure = UserBusinessStructure::where('year', $this->year)
->where('month', $this->month)
->first();
return ($structure && $structure->completed) ? $structure : null;
});
}
/**
* Lädt User-IDs aus gespeicherter Struktur
*/
public function getUserIdsFromStoredStructure(UserBusinessStructure $structure): array
{
$userIds = [];
if ($structure->structure) {
$this->extractUserIdsFromStructure((array) $structure->structure, $userIds);
}
if ($structure->parentless) {
foreach ($structure->parentless as $item) {
$userIds[] = $item->user_id;
}
}
return array_unique($userIds);
}
/**
* Rekursive Extraktion von User-IDs aus Struktur
*/
private function extractUserIdsFromStructure(array $structure, array &$userIds): void
{
foreach ($structure as $item) {
$userIds[] = $item->user_id;
if (isset($item->parents) && is_array($item->parents)) {
$this->extractUserIdsFromStructure($item->parents, $userIds);
}
}
}
/**
* Batch-Loading für User-Kollektionen
*/
public function loadUsersInBatches(array $userIds, int $batchSize = 100): \Generator
{
$chunks = array_chunk($userIds, $batchSize);
foreach ($chunks as $chunk) {
yield User::with([
'account',
'user_level',
'userBusiness' => function($query) {
$query->where('month', $this->month)
->where('year', $this->year);
}
])
->whereIn('id', $chunk)
->get()
->keyBy('id');
}
}
}

View file

@ -34,7 +34,7 @@ class TreeCalcBot
}
public function initStructureAdmin($check = true)
public function initStructureAdmin($check = true, $forceLiveCalculation = false)
{
//check is month is saved.
if($check && $UserBusinessStructure = self::isFromStored($this->date->month, $this->date->year)){
@ -46,7 +46,6 @@ class TreeCalcBot
$this->readParentsUsers();
$this->readParentlessUser();
}
}
public function initStructureUser($user_id)

View file

@ -0,0 +1,910 @@
<?php
namespace App\Services\BusinessPlan;
use App\User;
use stdClass;
use Carbon\Carbon;
use App\Models\UserBusinessStructure;
use Psr\Log\LoggerInterface;
/**
* Optimierte Version der TreeCalcBot Klasse
*
* Verbesserungen:
* - Trennung von Datenzugriff (Repository Pattern)
* - Trennung von HTML-Rendering (Renderer Pattern)
* - Optimierte Datenbankabfragen (N+1 Problem gelöst)
* - Memory-effiziente Verarbeitung großer Datenmengen
* - Robuste Fehlerbehandlung mit Logging
* - Dependency Injection für bessere Testbarkeit
*/
class TreeCalcBotOptimized
{
private stdClass $date;
private string $initFrom;
private array $businessUsers = [];
private array $parentless = [];
private ?BusinessUserItemOptimized $businessUser = null;
private ?BusinessUserItemOptimized $sponsor = null;
private array $processedUserIds = [];
private bool $forceLiveCalculation = false;
private BusinessUserRepository $repository;
private TreeHtmlRenderer $renderer;
private LoggerInterface $logger;
public function __construct(
int $month,
int $year,
string $initFrom = 'member',
bool $forceLiveCalculation = false,
?BusinessUserRepository $repository = null,
?TreeHtmlRenderer $renderer = null,
?LoggerInterface $logger = null
) {
$this->validateInput($month, $year);
$this->initializeDate($month, $year);
$this->initFrom = $initFrom;
$this->forceLiveCalculation = $forceLiveCalculation;
// Dependency Injection mit Fallback
$this->repository = $repository ?? new BusinessUserRepository($month, $year);
$this->renderer = $renderer ?? new TreeHtmlRenderer($initFrom, $forceLiveCalculation);
$this->logger = $logger ?? app(LoggerInterface::class);
}
/**
* Initialisiert die Business-Struktur für Admin-Ansicht
*
* @param bool $check Prüft auf gespeicherte Struktur
* @param bool $forceLiveCalculation Erzwingt Live-Berechnung und überspringt gespeicherte Daten
*/
public function initStructureAdmin(bool $check = true, bool $forceLiveCalculation = false): void
{
try {
$this->forceLiveCalculation = $forceLiveCalculation;
if ($forceLiveCalculation) {
$this->logger->info("Building fresh business structure for {$this->date->month}/{$this->date->year} with forced live calculation");
$this->buildFreshStructure();
return;
}
$storedStructure = null;
if ($check) {
$storedStructure = $this->repository->getStoredStructure();
}
if ($storedStructure) {
$this->logger->info("Loading stored business structure for {$this->date->month}/{$this->date->year}");
$this->loadStoredStructure($storedStructure);
} else {
$this->logger->info("Building fresh business structure for {$this->date->month}/{$this->date->year}");
$this->buildFreshStructure();
}
} catch (\Exception $e) {
$this->logger->error("Error initializing admin structure: " . $e->getMessage());
throw $e;
}
}
/**
* Initialisiert die Struktur für einen spezifischen User
*
* @param int $userId Die User-ID
* @param bool $forceLiveCalculation Erzwingt Live-Berechnung und überspringt gespeicherte Daten
*/
public function initStructureUser(int $userId, bool $forceLiveCalculation = false): void
{
try {
$this->forceLiveCalculation = $forceLiveCalculation;
if ($forceLiveCalculation) {
$this->logger->info("Initializing structure for user: {$userId} with forced live calculation");
} else {
$this->logger->info("Initializing structure for user: {$userId}");
}
$user = $this->repository->getUserWithRelations($userId);
if (!$user) {
$this->logger->warning("User not found: {$userId}");
return;
}
$businessUserItem = new BusinessUserItemOptimized($this->date);
$businessUserItem->makeUserFromModel($user); // Erst User-Model laden, ohne forceLiveCalculation
$this->addUserIdToProcessed($userId);
$this->businessUsers[] = $businessUserItem;
$this->logger->info("Created businessUserItem for user {$userId}, total businessUsers: " . count($this->businessUsers));
// Prüfe gespeicherte Struktur nur, wenn Live-Berechnung nicht erzwungen wird
$storedStructure = null;
if (!$forceLiveCalculation) {
$storedStructure = $this->repository->getStoredStructure();
$this->logger->info("Stored structure " . ($storedStructure ? "found" : "not found"));
}
if ($storedStructure && !$forceLiveCalculation) {
$this->loadStoredParentsUsers($storedStructure);
if (isset($this->businessUsers[0]) && $this->businessUsers[0]->sponsor) {
$this->loadStoredSponsorUser($this->businessUsers[0]->sponsor->user_id);
}
} else {
if ($forceLiveCalculation) {
$this->logger->info("Forcing live calculation - skipping stored structure for user {$userId}");
}
$this->loadParentsUsers();
$this->loadSponsorUser($userId);
$totalSubUsers = 0;
foreach ($this->businessUsers as $businessUser) {
$totalSubUsers += count($businessUser->businessUserItems);
}
$this->logger->info("After loadParentsUsers: {$totalSubUsers} total sub-users loaded across " . count($this->businessUsers) . " business users");
// WICHTIG: calcQualPP() erst NACH loadParentsUsers() aufrufen, da Points benötigt werden
if ($forceLiveCalculation) {
$this->logger->info("Calculating qualification levels for all business users");
foreach ($this->businessUsers as $businessUser) {
$businessUser->calcQualPP();
}
//wird nicht benötigt, da hier nur die Points berechnet werden
//$this->calculateQualPPForAllUsers(); // Auch für alle Sub-User
}
}
} catch (\Exception $e) {
$this->logger->error("Error initializing user structure for {$userId}: " . $e->getMessage());
throw $e;
}
}
/**
* Initialisiert detaillierte Business-User-Informationen
*
* @param User $user Das User-Model
* @param bool $forceLiveCalculation Erzwingt Live-Berechnung und überspringt gespeicherte Daten
*/
public function initBusinesslUserDetail(User $user, bool $forceLiveCalculation = false): void
{
try {
$this->logger->info("Initializing business user details for: {$user->id}");
$this->businessUser = new BusinessUserItemOptimized($this->date);
$this->businessUser->makeUserFromModel($user, $forceLiveCalculation); // ✅ Nutzt bereits User-Objekt
$this->businessUser->checkSponsor($user);
// Führe vollständige Berechnung durch, wenn:
// 1. Daten nicht gespeichert sind ODER
// 2. Live-Berechnung erzwungen wird
if (!$this->businessUser->isSave() || $forceLiveCalculation) {
if ($forceLiveCalculation) {
$this->logger->info("Forcing live calculation for user {$user->id}");
}
// Aufbau der Struktur für den User in die unendliche Tiefe
$this->businessUser->readParentsBusinessUsers($forceLiveCalculation);
// Calculate Points in Lines (optimiert für Memory-Effizienz)
if (count($this->businessUser->businessUserItems) > 0) {
$this->calculateUserPointsOptimized($this->businessUser->businessUserItems, 1, $this->businessUser);
}
// Qualifikation nach qual_kp (KundenPoints) und qual_pp (PaylinePoints)
$this->businessUser->calcQualPP();
}
} catch (\Exception $e) {
$this->logger->error("Error initializing business user details for {$user->id}: " . $e->getMessage());
throw $e;
}
}
/**
* Gibt Growth Bonus zurück (ab Linie 6)
* Erweitert um Array/Object-Kompatibilität für business_lines
*/
public function getGrowthBonus(): array
{
if (!$this->businessUser || !$this->businessUser->business_lines) {
return [];
}
if (count($this->businessUser->business_lines) > 6) {
// Handle both array and object types (JSON deserialization inconsistency)
if (is_array($this->businessUser->business_lines)) {
$bLines = $this->businessUser->business_lines;
} else {
$bLines = $this->businessUser->business_lines->toArray();
}
return array_slice($bLines, 6);
}
return [];
}
/**
* Gibt Wert für spezifische Linie zurück
*/
public function getKeybyLine(int $line, string $key)
{
if (!$this->businessUser || !$this->businessUser->business_lines) {
return 0;
}
$bLines = $this->businessUser->business_lines;
if (!isset($bLines[$line])) {
return 0;
}
$lineData = $bLines[$line];
if ($lineData instanceof stdClass) {
return $lineData->{$key} ?? 0;
}
if (is_array($lineData)) {
return $lineData[$key] ?? 0;
}
return 0;
}
/**
* HTML-Rendering Methoden (Delegation an Renderer)
*/
public function makeHtmlTree(): string
{
return $this->renderer->renderTree($this->businessUsers);
}
public function makeParentlessHtml(): string
{
return $this->renderer->renderParentless($this->parentless);
}
public function makeSponsorHtml(): string
{
return $this->renderer->renderSponsor($this->sponsor);
}
/**
* Getter-Methoden (Rückwärtskompatibilität)
*/
public function getItems(): array
{
return $this->businessUsers;
}
/**
* Zählt die Gesamtanzahl aller User in der Struktur (rekursiv)
*/
public function getTotalUserCount(): int
{
$totalCount = 0;
// Zähle alle Root-User
$totalCount += count($this->businessUsers);
// Zähle alle Unter-User rekursiv
foreach ($this->businessUsers as $businessUser) {
$totalCount += $this->countBusinessUserItems($businessUser);
}
// Zähle parentless User
$totalCount += count($this->parentless);
return $totalCount;
}
/**
* Zählt BusinessUserItems rekursiv
*/
private function countBusinessUserItems($businessUserItem): int
{
$count = 0;
if (isset($businessUserItem->businessUserItems) && is_array($businessUserItem->businessUserItems)) {
$count += count($businessUserItem->businessUserItems);
// Rekursiv durch alle Unter-Items zählen
foreach ($businessUserItem->businessUserItems as $subItem) {
$count += $this->countBusinessUserItems($subItem);
}
}
return $count;
}
public function isParentless(): bool
{
return !empty($this->parentless);
}
/**
* Static Methoden (Rückwärtskompatibilität)
*/
public static function isFromStored(int $month, int $year): ?UserBusinessStructure
{
$structure = UserBusinessStructure::where('year', $year)
->where('month', $month)
->first();
return ($structure && $structure->completed) ? $structure : null;
}
public static function addUserID(int $id): void
{
// Deprecated: Wird durch Instanz-Methode ersetzt
// Bleibt für Rückwärtskompatibilität erhalten
}
// ===== Private Methoden =====
/**
* Validiert Eingabeparameter
*/
private function validateInput(int $month, int $year): void
{
if ($month < 1 || $month > 12) {
throw new \InvalidArgumentException("Invalid month: {$month}");
}
$currentYear = (int) date('Y');
if ($year < 2020 || $year > $currentYear + 1) {
throw new \InvalidArgumentException("Invalid year: {$year}");
}
}
/**
* Initialisiert Datums-Objekt
*/
private function initializeDate(int $month, int $year): void
{
$this->date = new stdClass();
$date = Carbon::parse($year . '-' . $month . '-1');
$this->date->month = $month;
$this->date->year = $year;
$this->date->start_date = $date->format('Y-m-d H:i:s');
$this->date->end_date = $date->endOfMonth()->format('Y-m-d H:i:s');
}
/**
* Lädt gespeicherte Struktur
*/
private function loadStoredStructure(UserBusinessStructure $structure): void
{
$this->loadStoredRootUsers($structure);
$this->loadStoredParentsUsers($structure);
$this->loadStoredParentlessUsers($structure);
// Prüfe ob gespeicherte Daten vollständig sind, ansonsten berechne neu
$this->validateAndRecalculateIfNeeded();
$this->validateAndRecalculateParentlessIfNeeded();
}
/**
* Baut frische Struktur auf
*/
private function buildFreshStructure(): void
{
$this->loadRootUsers();
$this->loadParentsUsers();
$this->loadParentlessUsers();
// WICHTIG: Berechne Punkte und Qualifikationen für alle Business-Users
$this->calculateAllBusinessUsers();
$this->calculateAllParentlessUsers();
}
/**
* Lädt Root-Users (optimiert mit Memory-Monitoring)
*/
private function loadRootUsers(): void
{
$startMemory = memory_get_usage();
$users = $this->repository->getRootUsers();
foreach ($users as $user) {
// Memory-Check vor jeder User-Verarbeitung
$this->checkMemoryUsage('loadRootUsers', $user->id);
$businessUserItem = new BusinessUserItemOptimized($this->date);
$businessUserItem->makeUserFromModel($user, $this->forceLiveCalculation); // ✅ Nutzt bereits geladene Relations mit forceLiveCalculation
$this->addUserIdToProcessed($user->id);
$this->businessUsers[] = $businessUserItem;
}
$endMemory = memory_get_usage();
$memoryUsed = $this->formatBytes($endMemory - $startMemory);
$this->logger->info("Loaded " . count($users) . " root users with optimized relations. Memory used: {$memoryUsed}");
}
/**
* Lädt Parent-Users für alle Business-Users
*/
private function loadParentsUsers(): void
{
$this->logger->info("Loading parent users for " . count($this->businessUsers) . " business users");
foreach ($this->businessUsers as $businessUser) {
$businessUser->readParentsBusinessUsers($this->forceLiveCalculation);
$this->logger->debug("Loaded " . count($businessUser->businessUserItems) . " parent users for user " . ($businessUser->b_user->user_id ?? 'unknown'));
}
}
/**
* Lädt parentlose Users (Memory-optimiert)
*/
private function loadParentlessUsers(): void
{
$count = 0;
$excludeIds = array_keys($this->processedUserIds);
foreach ($this->repository->getParentlessUsers($excludeIds) as $user) {
$businessUserItem = new BusinessUserItemOptimized($this->date);
$businessUserItem->makeUserFromModel($user, $this->forceLiveCalculation); // ✅ Nutzt bereits geladene Relations mit forceLiveCalculation
$this->parentless[] = $businessUserItem;
$count++;
}
$this->logger->info("Loaded {$count} parentless users with optimized relations");
}
/**
* Berechnet Punkte und Qualifikationen für alle Business-Users
*/
private function calculateAllBusinessUsers(): void
{
$startTime = microtime(true);
$this->logger->info("Starting calculation for " . count($this->businessUsers) . " business users");
foreach ($this->businessUsers as $businessUser) {
try {
// Berechne Punkte in Linien (wie bei initBusinesslUserDetail)
if (count($businessUser->businessUserItems) > 0) {
$this->calculateUserPointsOptimized($businessUser->businessUserItems, 1, $businessUser);
}
// Qualifikation nach qual_kp und qual_pp berechnen
$businessUser->calcQualPP();
} catch (\Exception $e) {
$this->logger->error("Error calculating business user {$businessUser->__get('user_id')}: " . $e->getMessage());
// Weiter mit dem nächsten User, nicht abbrechen
continue;
}
}
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$this->logger->info("Completed calculations for all business users in {$executionTime}ms");
}
/**
* Berechnet Punkte und Qualifikationen für alle Parentless-Users
*/
private function calculateAllParentlessUsers(): void
{
if (empty($this->parentless)) {
return;
}
$startTime = microtime(true);
$this->logger->info("Starting calculation for " . count($this->parentless) . " parentless users");
foreach ($this->parentless as $parentlessUser) {
try {
// Berechne Punkte in Linien
if (count($parentlessUser->businessUserItems) > 0) {
$this->calculateUserPointsOptimized($parentlessUser->businessUserItems, 1, $parentlessUser);
}
// Qualifikation berechnen
$parentlessUser->calcQualPP();
} catch (\Exception $e) {
$this->logger->error("Error calculating parentless user {$parentlessUser->__get('user_id')}: " . $e->getMessage());
continue;
}
}
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$this->logger->info("Completed calculations for all parentless users in {$executionTime}ms");
}
/**
* Validiert gespeicherte Daten und berechnet bei Bedarf neu
*/
private function validateAndRecalculateIfNeeded(): void
{
$incompleteUsers = 0;
foreach ($this->businessUsers as $businessUser) {
// Prüfe ob grundlegende Berechnungen vorhanden sind
if ($this->isBusinessUserIncomplete($businessUser)) {
$incompleteUsers++;
try {
// Führe fehlende Berechnungen durch
if (count($businessUser->businessUserItems) > 0) {
$this->calculateUserPointsOptimized($businessUser->businessUserItems, 1, $businessUser);
}
$businessUser->calcQualPP();
} catch (\Exception $e) {
$this->logger->error("Error recalculating business user {$businessUser->__get('user_id')}: " . $e->getMessage());
}
}
}
if ($incompleteUsers > 0) {
$this->logger->info("Recalculated {$incompleteUsers} incomplete business users from stored data");
}
}
/**
* Prüft ob ein BusinessUser unvollständige Daten hat
* Erweitert um Level-Qualifikationsdaten für Struktur-Ansicht
*/
private function isBusinessUserIncomplete($businessUser): bool
{
// Prüfe grundlegende Felder die nach Berechnungen vorhanden sein sollten
$salesVolumeSum = $businessUser->__get('sales_volume_points_sum');
$qualKp = $businessUser->__get('qual_kp');
// Prüfe Level-Qualifikationsdaten für Struktur-Ansicht
$nextQualUserLevel = $businessUser->__get('next_qual_user_level');
$nextCanUserLevel = $businessUser->__get('next_can_user_level');
$hasLevelQualificationData = !empty($nextQualUserLevel) || !empty($nextCanUserLevel);
// User ist unvollständig wenn:
// 1. Grundlegende berechnete Werte fehlen ODER
// 2. Level-Qualifikationsdaten fehlen (wichtig für Struktur-Ansicht mit grünen Pfeilen)
$missingBasicData = ($salesVolumeSum === null || $salesVolumeSum === 0) &&
($qualKp === null || $qualKp === 0);
$missingLevelData = !$hasLevelQualificationData;
return $missingBasicData || $missingLevelData;
}
/**
* Validiert und berechnet parentless Users bei Bedarf neu
*/
private function validateAndRecalculateParentlessIfNeeded(): void
{
if (empty($this->parentless)) {
return;
}
$incompleteUsers = 0;
foreach ($this->parentless as $parentlessUser) {
if ($this->isBusinessUserIncomplete($parentlessUser)) {
$incompleteUsers++;
try {
if (count($parentlessUser->businessUserItems) > 0) {
$this->calculateUserPointsOptimized($parentlessUser->businessUserItems, 1, $parentlessUser);
}
$parentlessUser->calcQualPP();
} catch (\Exception $e) {
$this->logger->error("Error recalculating parentless user {$parentlessUser->__get('user_id')}: " . $e->getMessage());
}
}
}
if ($incompleteUsers > 0) {
$this->logger->info("Recalculated {$incompleteUsers} incomplete parentless users from stored data");
}
}
/**
* Lädt Sponsor für User
*/
private function loadSponsorUser(int $userId): void
{
try {
$sponsorUser = $this->repository->getSponsorForUser($userId);
if ($sponsorUser) {
$this->sponsor = new BusinessUserItemOptimized($this->date);
$this->sponsor->makeUser($sponsorUser->id);
$this->logger->info("Loaded sponsor {$sponsorUser->id} for user {$userId}");
}
} catch (\Exception $e) {
$this->logger->warning("Could not load sponsor for user {$userId}: " . $e->getMessage());
}
}
/**
* Gespeicherte Root-Users laden
*/
private function loadStoredRootUsers(UserBusinessStructure $structure): void
{
if (!$structure->structure) {
return;
}
foreach ($structure->structure as $obj) {
$businessUserItem = new BusinessUserItemOptimized($this->date);
$businessUserItem->makeUser($obj->user_id);
$this->addUserIdToProcessed($obj->user_id);
$this->businessUsers[] = $businessUserItem;
}
}
/**
* Gespeicherte Parent-Users laden
*/
private function loadStoredParentsUsers(UserBusinessStructure $structure): void
{
foreach ($this->businessUsers as $businessUser) {
$businessUser->readStoredParentsBusinessUsers($structure->structure);
}
}
/**
* Gespeicherte parentlose Users laden
*/
private function loadStoredParentlessUsers(UserBusinessStructure $structure): void
{
if (!$structure->parentless) {
return;
}
foreach ($structure->parentless as $obj) {
if (!isset($this->processedUserIds[$obj->user_id])) {
$businessUserItem = new BusinessUserItemOptimized($this->date);
$businessUserItem->makeUser($obj->user_id);
$this->parentless[] = $businessUserItem;
}
}
}
/**
* Gespeicherten Sponsor laden
*/
private function loadStoredSponsorUser(int $userId): void
{
$this->sponsor = new BusinessUserItemOptimized($this->date);
$this->sponsor->makeUser($userId);
}
/**
* Optimierte Punkte-Berechnung (Stack-basiert mit korrekter Depth-First Reihenfolge)
*
* KRITISCH: Stack muss gleiche Reihenfolge wie Original-Rekursion produzieren
* Original: Depth-First Traversierung (erst tief, dann Punkte addieren)
* Stack: Muss umgekehrt arbeiten - erst alle Kinder sammeln, dann von tief zu flach verarbeiten
*/
private function calculateUserPointsOptimized(array $businessUserItems, int $startLine, BusinessUserItemOptimized $businessUserToUpdate): void
{
$processingStack = [];
$collectionStack = []; // Sammelt Items in korrekter Reihenfolge
// Phase 1: Sammle alle Items in Depth-First Reihenfolge
foreach ($businessUserItems as $item) {
$collectionStack[] = ['item' => $item, 'line' => $startLine, 'depth' => 0];
}
// Expandiere alle Kinder (Depth-First)
$processedItems = [];
while (!empty($collectionStack)) {
$current = array_shift($collectionStack); // FIFO für Breadth-First Sammlung
$item = $current['item'];
$line = $current['line'];
$depth = $current['depth'];
// Markiere für Verarbeitung (mit Tiefe für spätere Sortierung)
$processingStack[] = [
'item' => $item,
'line' => $line,
'depth' => $depth,
'id' => $item->user_id ?? uniqid()
];
// Füge Kinder hinzu (werden später verarbeitet = Depth-First)
if (isset($item->businessUserItems) && count($item->businessUserItems) > 0) {
// Kinder in umgekehrter Reihenfolge hinzufügen für korrekte Stack-Verarbeitung
$children = array_reverse($item->businessUserItems);
foreach ($children as $childItem) {
array_unshift($collectionStack, [
'item' => $childItem,
'line' => $line + 1,
'depth' => $depth + 1
]);
}
}
}
// Phase 2: Sortiere nach Tiefe (tiefste zuerst, wie bei Rekursion)
usort($processingStack, function($a, $b) {
return $b['depth'] <=> $a['depth']; // Tiefste zuerst
});
// Phase 3: Verarbeite in korrekter Reihenfolge (von tief zu flach)
foreach ($processingStack as $current) {
$item = $current['item'];
$line = $current['line'];
try {
// Business Line initialisieren falls nötig
if (!isset($businessUserToUpdate->business_lines[$line])) {
$obj = new stdClass();
$obj->points = 0;
$businessUserToUpdate->addBusinessLineToUser($line, $obj);
}
// Punkte hinzufügen (mit Validierung)
$points = (float) ($item->sales_volume_points_TP_sum ?? 0);
if ($points > 0) {
$businessUserToUpdate->addBusinessLinePoints($line, $points);
$businessUserToUpdate->addTotalTP($points);
}
$this->logger->debug("Processed user {$current['id']} at line {$line} with {$points} points");
} catch (\Exception $e) {
$this->logger->error("Error processing user points for {$current['id']}: " . $e->getMessage());
}
}
$this->logger->info("Processed " . count($processingStack) . " business user items in depth-first order");
}
/**
* User-ID zu verarbeiteten IDs hinzufügen
*/
private function addUserIdToProcessed(int $id): void
{
$this->processedUserIds[$id] = true;
}
/**
* Prüft ob User bereits verarbeitet wurde
*/
private function isUserProcessed(int $id): bool
{
return isset($this->processedUserIds[$id]);
}
/**
* Memory-Monitoring Methoden
*/
private function checkMemoryUsage(string $operation, $identifier = null): void
{
$currentMemory = memory_get_usage();
$memoryLimit = $this->parseMemoryLimit(ini_get('memory_limit'));
$memoryPercent = ($currentMemory / $memoryLimit) * 100;
if ($memoryPercent > 80) {
$currentFormatted = $this->formatBytes($currentMemory);
$limitFormatted = $this->formatBytes($memoryLimit);
$this->logger->warning("High memory usage detected in {$operation}", [
'identifier' => $identifier,
'current_memory' => $currentFormatted,
'memory_limit' => $limitFormatted,
'usage_percent' => round($memoryPercent, 2)
]);
// Garbage Collection bei hohem Memory-Verbrauch
if ($memoryPercent > 90) {
$this->logger->warning("Critical memory usage - forcing garbage collection");
gc_collect_cycles();
}
}
}
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;
}
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];
}
/**
* Public Properties für Rückwärtskompatibilität
*/
public function __get(string $name)
{
switch ($name) {
case 'date':
return $this->date;
case 'business_user':
return $this->businessUser;
case 'business_users':
return $this->businessUsers;
case 'parentless':
return $this->parentless;
default:
throw new \InvalidArgumentException("Property {$name} does not exist");
}
}
/**
* Berechnet calcQualPP() für alle BusinessUsers rekursiv
* Muss NACH loadParentsUsers() aufgerufen werden, da Points benötigt werden
*/
private function calculateQualPPForAllUsers(): void
{
$this->logger->info("Starting recursive calcQualPP for all users");
$totalCalculated = 0;
foreach ($this->businessUsers as $businessUser) {
$totalCalculated += $this->calculateQualPPRecursive($businessUser);
}
$this->logger->info("Completed calcQualPP for {$totalCalculated} users");
}
/**
* Rekursive Hilfsmethode für calcQualPP
*/
private function calculateQualPPRecursive($businessUser): int
{
$calculated = 0;
if (isset($businessUser->businessUserItems) && is_array($businessUser->businessUserItems)) {
foreach ($businessUser->businessUserItems as $subBusinessUser) {
if ($subBusinessUser->b_user && $subBusinessUser->b_user->user_id) {
try {
$subBusinessUser->calcQualPP();
$calculated++;
$this->logger->debug("Calculated calcQualPP for user " . $subBusinessUser->b_user->user_id);
} catch (\Exception $e) {
$this->logger->warning("Error calculating calcQualPP for user " . $subBusinessUser->b_user->user_id . ": " . $e->getMessage());
}
// Rekursiver Aufruf
$calculated += $this->calculateQualPPRecursive($subBusinessUser);
}
}
}
return $calculated;
}
public function __set(string $name, $value)
{
switch ($name) {
case 'business_users':
$this->businessUsers = $value;
break;
case 'parentless':
$this->parentless = $value;
break;
default:
throw new \InvalidArgumentException("Property {$name} cannot be set");
}
}
}

View file

@ -0,0 +1,183 @@
<?php
namespace App\Services\BusinessPlan;
use App\User;
use App\Models\UserBusiness;
/**
* Klasse für die HTML-Darstellung von Business-Trees
* Trennt Präsentationslogik von Geschäftslogik
*/
class TreeHelperOptimized
{
/**
* Generiert QualKP Badge für UserBusiness
*/
public static function generateQualKPBadge(UserBusiness $userBusiness): string
{
if (!$userBusiness->m_level_id) {
return '-';
}
$qualKP = (int) $userBusiness->qual_kp;
$pointsSum = (int) $userBusiness->sales_volume_points_KP_sum;
$isQual = $pointsSum >= $qualKP;
$badgeClass = $isQual ? 'badge-outline-success' : 'badge-outline-danger';
return '<span class="badge ' . $badgeClass . '"> KU ' . $qualKP . '</span>';
}
/**
* Generiert QualKP Badge für User
*/
public static function generateQualKPBadgeForUser(User $user, int $month, int $year): string
{
if (!$user->user_level) {
return '-';
}
$qualKP = (int) $user->user_level->qual_kp;
$pointsSum = (int) $user->getUserSalesVolumeBy($month, $year, 'sales_volume_points_KP_sum');
$isQual = $pointsSum >= $qualKP;
$badgeClass = $isQual ? 'badge-outline-success' : 'badge-outline-warning-dark';
return '<span class="badge ' . $badgeClass . '"> KU ' . $qualKP . '</span>';
}
/**
* Generiert Sales Volume Display für UserBusiness
*/
public static function generateSalesVolumeDisplay(UserBusiness $userBusiness, string $type): string
{
if ($type === 'points') {
$total = (int) $userBusiness->sales_volume_points_KP_sum;
$individual = (int) $userBusiness->sales_volume_KP_points;
$shop = (int) $userBusiness->sales_volume_points_shop;
} else {
$total = (float) $userBusiness->sales_volume_total_sum;
$individual = (float) $userBusiness->sales_volume_total;
$shop = (float) $userBusiness->sales_volume_total_shop;
$suffix = ' &euro;';
}
$totalFormatted = $type === 'points' ? $total : formatNumber($total);
$individualFormatted = $type === 'points' ? $individual : formatNumber($individual);
$shopFormatted = $type === 'points' ? $shop : formatNumber($shop);
$suffix = $type === 'points' ? '' : ' &euro;';
return '<div class="no-line-break">' . $totalFormatted . $suffix . '</div>' .
'<span class="small no-line-break">E: ' . $individualFormatted . ' | S: ' . $shopFormatted . $suffix . '</span>';
}
/**
* Generiert Sales Volume Display für User
*/
public static function generateSalesVolumeDisplayForUser(User $user, string $type, int $month, int $year): string
{
if ($type === 'points') {
$total = (int) $user->getUserSalesVolumeBy($month, $year, 'sales_volume_points_KP_sum');
$individual = (int) $user->getUserSalesVolumeBy($month, $year, 'sales_volume_KP_points');
$shop = (int) $user->getUserSalesVolumeBy($month, $year, 'sales_volume_points_shop');
} else {
$total = (float) $user->getUserSalesVolumeBy($month, $year, 'sales_volume_total_sum');
$individual = (float) $user->getUserSalesVolumeBy($month, $year, 'sales_volume_total');
$shop = (float) $user->getUserSalesVolumeBy($month, $year, 'sales_volume_total_shop');
}
$totalFormatted = $type === 'points' ? $total : formatNumber($total);
$individualFormatted = $type === 'points' ? $individual : formatNumber($individual);
$shopFormatted = $type === 'points' ? $shop : formatNumber($shop);
$suffix = $type === 'points' ? '' : ' &euro;';
return '<div class="no-line-break">' . $totalFormatted . $suffix . '</div>' .
'<span class="small no-line-break">E: ' . $individualFormatted . ' | S: ' . $shopFormatted . $suffix . '</span>';
}
/**
* Generiert Action Buttons (mit XSS-Schutz)
*/
public static function generateActionButtons($userId): string
{
$userId = (int) $userId; // Sicherheit: Nur Integer
$html = '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
data-id="' . $userId . '"
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>';
if (config('app.debug') === true) {
$html .= '<a href="' . route('admin_business_optimized_user_detail', [$userId]) . '" class="btn icon-btn btn-xs btn-primary"><span class="fa fa-calculator"></span></a>';
}
return $html;
}
/**
* Generiert Sponsor Display für UserBusiness
*/
public static function generateSponsorDisplay(UserBusiness $userBusiness): string
{
if (!$userBusiness->sponsor || !$userBusiness->sponsor->is_sponsor) {
return '-';
}
$sponsor = $userBusiness->sponsor;
$html = e($sponsor->first_name . ' ' . $sponsor->last_name);
$html .= ' &nbsp;<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
data-id="' . (int) $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>';
$html .= '<span class="small no-line-break">' . e($sponsor->email);
$html .= ' | ' . e($sponsor->m_account);
$html .= '</span>';
return $html;
}
/**
* Generiert Sponsor Display für User
*/
public static function generateSponsorDisplayForUser(User $user): string
{
if (!$user->user_sponsor) {
return '-';
}
$sponsor = $user->user_sponsor;
$html = '';
if ($sponsor->account) {
$html .= e($sponsor->account->first_name . ' ' . $sponsor->account->last_name);
$html .= ' &nbsp;<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
data-id="' . (int) $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>';
}
$html .= '<span class="small no-line-break">' . e($sponsor->email);
if ($sponsor->account) {
$html .= ' | ' . e($sponsor->account->m_account);
}
$html .= '</span>';
return $html;
}
}

View file

@ -0,0 +1,391 @@
<?php
namespace App\Services\BusinessPlan;
use App\Services\TranslationHelper;
/**
* Klasse für die HTML-Darstellung von Business-Trees
* Trennt Präsentationslogik von Geschäftslogik
*/
class TreeHtmlRenderer
{
private string $initFrom;
private bool $forceLiveCalculation;
public function __construct(string $initFrom = 'member', bool $forceLiveCalculation = false)
{
$this->initFrom = $initFrom;
$this->forceLiveCalculation = $forceLiveCalculation;
}
/**
* Rendert den kompletten Business-Tree als HTML
*/
public function renderTree(array $businessUsers): string
{
if (empty($businessUsers)) {
return '<div class="alert alert-info">Keine Business-User gefunden.</div>';
}
$html = '<ol class="dd-list">';
foreach ($businessUsers as $businessUser) {
$html .= $this->renderUserItem($businessUser, 0);
}
$html .= '</ol>';
return $html;
}
/**
* Rendert parentlose User als HTML
*/
public function renderParentless(array $parentless): string
{
if (empty($parentless)) {
return '<div class="alert alert-info">Keine parentlosen User gefunden.</div>';
}
$html = '';
foreach ($parentless as $item) {
$html .= $this->renderParentlessItem($item);
}
return $html;
}
/**
* Rendert Sponsor-Information als HTML
*/
public function renderSponsor($sponsor): string
{
if (!$sponsor) {
return '<div class="alert alert-warning">' . __('team.no_sponsor_assigned') . '</div>';
}
return '<li class="dd-item dd-nodrag" data-id="">' .
'<div class="dd-handle">' .
$this->renderUserInfo($sponsor, false, true) .
'</div>' .
'</li>';
}
/**
* Rendert User Team Tree (für UserTeamCalcBot)
*/
public function renderUserTeamTree(array $teamMembers): string
{
if (empty($teamMembers)) {
return '<div class="alert alert-info">Keine Team-Mitglieder gefunden.</div>';
}
$html = '<ol class="dd-list">';
foreach ($teamMembers as $member) {
$html .= $this->renderTeamMemberItem($member, 0);
}
$html .= '</ol>';
return $html;
}
/**
* Rendert User Sponsor (für UserTeamCalcBot)
*/
public function renderUserSponsor(\App\User $sponsor): string
{
if (!$sponsor || !$sponsor->account) {
return '<div class="alert alert-info">Kein Sponsor gefunden.</div>';
}
$html = '<div class="dd-item">';
$html .= '<div class="dd-handle">';
$html .= '<div class="row">';
// Sponsor Info
$html .= '<div class="col-md-3">';
$html .= '<strong>' . e($sponsor->account->first_name . ' ' . $sponsor->account->last_name) . '</strong><br>';
$html .= '<small>' . e($sponsor->email) . '</small>';
$html .= '</div>';
// Account Info
$html .= '<div class="col-md-2">';
$html .= '<span class="badge badge-secondary">' . e($sponsor->account->m_account ?? '') . '</span>';
$html .= '</div>';
// Level Info
$html .= '<div class="col-md-2">';
if ($sponsor->user_level) {
$html .= '<span class="badge badge-primary">' . e($sponsor->user_level->getLang('name')) . '</span>';
}
$html .= '</div>';
// Status
$html .= '<div class="col-md-2">';
$html .= get_active_badge($sponsor->isActiveAccount());
$html .= '</div>';
$html .= '</div>';
$html .= '</div>';
$html .= '</div>';
return $html;
}
/**
* Rendert einzelnes Team-Mitglied
*/
private function renderTeamMemberItem($member, int $depth): string
{
$html = '<li class="dd-item" data-id="' . ($member->user_id ?? 0) . '">';
$html .= '<div class="dd-handle">';
$html .= '<div class="row">';
// Einrückung basierend auf Tiefe
$indent = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $depth);
// Name und Email
$html .= '<div class="col-md-3">';
$html .= $indent;
$html .= '<strong>' . e(($member->first_name ?? '') . ' ' . ($member->last_name ?? '')) . '</strong><br>';
$html .= $indent . '<small>' . e($member->email ?? '') . '</small>';
$html .= '</div>';
// Account ID
$html .= '<div class="col-md-2">';
$html .= '<span class="badge badge-secondary">' . e($member->m_account ?? '') . '</span>';
$html .= '</div>';
// Level
$html .= '<div class="col-md-2">';
if (!empty($member->user_level_name)) {
$html .= '<span class="badge badge-primary">' . e($member->user_level_name) . '</span>';
if ($member->next_qual_user_level) {
$html .= '<span class="badge badge-outline-success ml-2"><i class="fa fa-arrow-up text-success" title="Karriere-Level erreicht!"></i></span>';
}
}
$html .= '</div>';
// Qualifikation
$html .= '<div class="col-md-2">';
if (!empty($member->qual_kp)) {
$pointsSum = (int) ($member->sales_volume_points_KP_sum ?? 0);
$qualKP = (int) $member->qual_kp;
$isQual = $pointsSum >= $qualKP;
$badgeClass = $isQual ? 'badge-success' : 'badge-warning';
$html .= '<span class="badge ' . $badgeClass . '">KU ' . $qualKP . '</span>';
}
$html .= '</div>';
// Status
$html .= '<div class="col-md-2">';
$html .= get_active_badge($member->active_account ?? 0);
$html .= '</div>';
$html .= '</div>';
$html .= '</div>';
// Kinder rendern
if (!empty($member->businessUserItems) && is_array($member->businessUserItems)) {
$html .= '<ol class="dd-list">';
foreach ($member->businessUserItems as $child) {
$html .= $this->renderTeamMemberItem($child, $depth + 1);
}
$html .= '</ol>';
}
$html .= '</li>';
return $html;
}
/**
* Rendert einen einzelnen User-Item mit Hierarchie
*/
private function renderUserItem($item, int $deep): string
{
$childrenHtml = '';
if ($item->businessUserItems && count($item->businessUserItems) > 0) {
$childrenHtml = '<ol class="dd-list dd-nodrag">';
foreach ($item->businessUserItems as $child) {
$childrenHtml .= $this->renderUserItem($child, $deep + 1);
}
$childrenHtml .= '</ol>';
}
return '<li class="dd-item dd-nodrag" data-id="' . $item->user_id . '">' .
'<div class="dd-handle">' .
$this->renderUserCardWithDepth($item, $deep) .
'</div>' .
$childrenHtml .
'</li>';
}
/**
* Rendert parentlosen User-Item
*/
private function renderParentlessItem($item): string
{
return '<li class="dd-item dd-nodrag" data-id="' . $item->user_id . '">' .
'<div class="dd-handle">' .
$this->renderUserInfo($item, true, false) .
'</div>' .
'</li>';
}
/**
* Rendert User-Card mit Tiefe-Anzeige
*/
private function renderUserCardWithDepth($item, int $deep): string
{
$depthBadge = '';
if ($deep > 0) {
$depthBadge = '<div class="d-flex flex-column justify-content-center align-items-center">' .
'<div class="text-large font-weight-bolder line-height-1 my-2 text-secondary badge badge-outline-secondary">' . $deep . '</div>' .
'</div>';
}
return '<div class="media align-items-center">' .
$depthBadge .
'<div class="media-body ml-2">' .
$this->renderUserInfo($item, false, false) .
'</div>' .
'</div>';
}
/**
* Rendert die Basis-User-Informationen
*/
private function renderUserInfo($item, bool $showSponsor = false, bool $isSponsor = false): string
{
$statusClass = $item->active_account ? '' : 'text-muted';
$iconClass = $item->active_account ? 'text-primary' : 'text-danger';
\Log::debug("TreeHtmlRenderer: Rendering user info for user {$item->user_id}");
$html = '<span class="' . $statusClass . '">';
// User Link
$html .= '<a href="#" class="text-black" data-toggle="modal" data-target="#modals-load-content" ' .
'data-id="' . $item->user_id . '" data-action="business-user-show" data-back="" ' .
'data-modal="modal-md" data-init_from="' . $this->initFrom . '" data-route="' . route('modal_load') . '">' .
'<span class="mr-1 ion ion-ios-contact ' . $iconClass . '"></span> ' .
'<strong>' . e($item->first_name . ' ' . $item->last_name) . '</strong>' .
'</a>';
// Email
$html .= ' <a href="mailto:' . e($item->email) . '">' . e($item->email) . '</a>';
// Optional: Geburtstag
$birthday = $item->user_birthday; // Magic Method __get() verwenden
if ($birthday && trim($birthday) !== '') {
$html .= ' | <i class="ion ion-ios-gift text-primary"></i> ' . e($birthday);
}
// Optional: Telefon
$phone = $item->user_phone; // Magic Method __get() verwenden
if ($phone && trim($phone) !== '') {
$html .= ' | <i class="ion ion-ios-call text-primary"></i> ' . e($phone);
}
// Level Badge
$levelName = $item->user_level_name ? TranslationHelper::transUserLevelName($item->user_level_name) : '';
$account = $item->m_account ?: '';
$html .= ' <span class="badge badge-outline-default ' . $statusClass . '">' . e($levelName . ' | ' . $account) . '</span>';
// Karriere-Aufstiegs-Icon für qualifizierte User (nur in Struktur-Ansicht)#
if ($item->next_qual_user_level) {
$html .= '<span class="badge badge-outline-success ml-2"><i class="fa fa-arrow-up text-success" title="Karriere-Level erreicht!"></i></span>';
}
// Details für aktive Accounts
if ($item->active_account) {
$html .= '<br><span class="small">';
if(!$isSponsor){
$html .= $this->renderAccountDetails($item);
}
// Action Button (außer für Sponsor-Ansicht)
if (!$isSponsor && $this->shouldShowActionButton()) {
$html .= $this->renderActionButton($item->user_id);
}
$html .= '</span>';
} else {
// Inaktive Accounts
$paymentDate = $item->payment_account_date ?: '';
$html .= '<br><span class="small">' . __('team.account_to') . ': ' . e($paymentDate) . '</span>';
}
// Sponsor für parentlose User
if ($showSponsor && $item->m_sponsor_name) {
$html .= '<br>' . e($item->m_sponsor_name);
}
$html .= '</span>';
return $html;
}
/**
* Rendert Account-Details (Punkte, Umsatz)
*/
private function renderAccountDetails($item): string
{
$totalPoints = $item->sales_volume_points_KP_sum ?: 0;
$ePoints = $item->sales_volume_KP_points ?: 0;
$sPoints = $item->sales_volume_points_shop ?: 0;
$totalSum = $item->sales_volume_total_sum ?: 0;
$eSum = $item->sales_volume_total ?: 0;
$sSum = $item->sales_volume_total_shop ?: 0;
return '<strong>' . __('team.total_points') . ': ' . $totalPoints . '</strong> | ' .
__('team.e') . ': ' . $ePoints . ' | ' .
__('team.s') . ': ' . $sPoints . ' <strong> | ' .
__('team.net_turnover') . ': ' . formatNumber($totalSum) . ' &euro;</strong> | ' .
__('team.e') . ': ' . formatNumber($eSum) . ' &euro; | ' .
__('team.s') . ': ' . formatNumber($sSum) . ' &euro;';
}
/**
* Rendert Action-Button für User-Details
*/
private function renderActionButton(int $userId): string
{
return ' | <button type="button" class="btn icon-btn btn-xs btn-secondary" ' .
'data-toggle="modal" data-target="#modals-load-content" ' .
'data-id="' . $userId . '" data-action="business-user-detail" ' .
'data-back="" data-modal="modal-xl" ' .
'data-init_from="' . $this->initFrom . '" ' .
'data-live="' . $this->forceLiveCalculation . '" ' .
'data-optimized="1" ' .
'data-route="' . route('modal_load') . '">' .
'<span class="fa fa-calculator"></span>' .
'</button>';
}
/**
* Prüft ob Action-Button angezeigt werden soll
*/
private function shouldShowActionButton(): bool
{
try {
return ($this->initFrom === 'admin' && \Auth::check() && \Auth::user()->isAdmin()) ||
($this->initFrom === 'member');
} catch (\Exception $e) {
// Fallback for tests or when no user is authenticated
return $this->initFrom === 'member';
}
}
/**
* Setzt den Kontext für die Darstellung
*/
public function setInitFrom(string $initFrom): self
{
$this->initFrom = $initFrom;
return $this;
}
}

View file

@ -1,172 +0,0 @@
<?php
namespace App\Services\BusinessPlan;
use App\User;
use stdClass;
use Carbon\Carbon;
use App\Models\UserLevel;
use App\Models\UserBusiness;
class TreeUserItem
{
public $items = [];
private $date;
public $lines = [];
public $user_level_active;
public function __construct($date)
{
$this->date = $date;
return $this;
}
public function makeUser(User $user){
$this->user_level_active = $user->user_level ? $user->user_level : null;
$this->b_user = new UserBusiness();
$fill = [
'user_id' => $user->id,
'month' => $this->date->month,
'year' => $this->date->year,
'm_level' => $user->m_level,
'm_sponsor' => $user->m_sponsor,
'user_level_name' => $user->user_level ? $user->user_level->name : '',
'active_account' => $user->payment_account ? Carbon::parse($user->payment_account)->gt(Carbon::parse($this->date->start_date)) : false,
'payment_account_date' => $user->payment_account ? $user->getPaymentAccountDateFormat(false) : NULL,
'active_date' => $user->active_date ? $user->active_date : NULL,
'm_account' => $user->account->m_account,
'email' => $user->email,
'first_name' => $user->account->first_name,
'last_name' => $user->account->last_name,
'sales_volume_points' => $user->getUserSalesVolumeBy($this->date->month, $this->date->year, 'sales_volume_points'),
'sales_volume_points_shop' => $user->getUserSalesVolumeBy($this->date->month, $this->date->year, 'sales_volume_points_shop'),
'sales_volume_points_sum' => $user->getUserSalesVolumeBy($this->date->month, $this->date->year, 'sales_volume_points_sum'),
'sales_volume_total' => $user->getUserSalesVolumeBy($this->date->month, $this->date->year, 'sales_volume_total'),
'sales_volume_total_shop' => $user->getUserSalesVolumeBy($this->date->month, $this->date->year, 'sales_volume_total_shop'),
'sales_volume_total_sum' => $user->getUserSalesVolumeBy($this->date->month, $this->date->year, 'sales_volume_total_sum'),
'margin' => $user->user_level_active ? $user->user_level_active->margin : 0,
'margin_shop' => $user->user_level_active ? $user->user_level_active->margin_shop : 0,
'qual_kp' => $user->user_level_active ? $user->user_level_active->qual_kp : 0,
'qual_tp' => $user->user_level_active ? $user->user_level_active->qual_tp : 0,
];
$this->b_user->fill($fill);
}
public function addUserID(){
TreeCalcBot::addUserID($this->user_id);
}
public function isQualKP(){
return ($this->sales_volume_points_sum >= $this->qual_kp) ? true : false;
}
public function getRestQualKP(){
return $this->sales_volume_points_sum - $this->qual_kp;
}
public function checkSponsor(){
if($this->m_sponsor === null){
$this->m_sponsor_name = 'Keinen Sponsor zugewiesen';
return;
}
$user = User::find($this->m_sponsor);
if($user){
if($user->account){
$this->m_sponsor_name = substr('Sponsor: '.$user->account->first_name.' '.$user->account->last_name.' | '.$user->email.' | '.$user->account->m_account, 0, 190);
}else{
$this->m_sponsor_name = 'Sponsor: '.$user->email;
}
return;
}
$this->m_sponsor_name = 'Sponsor wurde gelöscht.';
return;
}
/*
'total_tp' => ,
'total_qual_tp' => ,
'commission_total' => ,
'lines',
'items',
'qual_user_level_id'
*/
public function readParentsUser(){
$users = User::with('account')->select('users.*')
->where('users.deleted_at', '=', null)
->where('users.id', '!=', 1)
->where('users.admin', "<", 4)
->where('users.m_level', "!=", null)
->where('users.m_sponsor', "=", $this->user_id) //<- need the id for parents / sponsors
->where('users.payment_account', "!=", null)
->where('users.active_date', "<=", $this->date->end_date)
->get();
dd($users);
if($users){
foreach($users as $user){
$TreeUserItem = new TreeUserItem($this->date);
$TreeUserItem->makeUser($user);
$TreeUserItem->addUserID();
$this->items[] = $TreeUserItem;
}
}
foreach($this->items as $item){
$item->readParentsUser();
}
}
public function calcUserTP($line){
if(!isset($this->lines[$line])){
$this->lines[$line] = new stdClass();
$this->lines[$line]->points = 0;
}
foreach($this->items as $item){
if(count($item->items) > 0){
$this->calcUserTP($line+1);
}
$this->lines[$line]->points += $item->sales_volume_points_sum;
$this->total_tp += $item->sales_volume_points_sum;
}
}
public function calcQualTP(){
if($this->isQualKP()){
$this->total_qual_tp = $this->total_tp + $this->getRestQualKP();
$this->qual_user_level = UserLevel::where('qual_tp', '<=', $this->total_qual_tp)->where('pos', '<=', $this->user->user_level->pos)->orderBy('qual_tp', 'desc')->first();
$this->commission_total = 0;
if($this->qual_user_level){
foreach($this->lines as $line => $values){
$values->margin = $this->qual_user_level->{'pr_line_'.$line};
if($line > 6){
//wachstumsbonus
$values->margin = $this->qual_user_level->growth_bonus;
}
$values->commission = round($values->points / 100 * $values->margin, 2);
$this->commission_total += $values->commission;
$this->lines[$line] = $values;
}
}
}
}
public function __get($property) {
if (property_exists($this->b_user, $property)) {
return $this->b_user->$property;
}
if (isset($this->b_user->{$property})) {
return $this->b_user->{$property};
}
}
}

View file

@ -0,0 +1,296 @@
<?php
namespace App\Services;
use App\Models\UserShop;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
/**
* Domain Service - Centralized domain and subdomain management
*
* This service provides a centralized way to handle domain resolution,
* subdomain validation, and domain-specific configuration management.
*/
class DomainService
{
private const CACHE_TTL = 3600; // 1 hour
private const CACHE_TAG_USER_SHOPS = 'user_shops';
private const CACHE_TAG_DOMAIN_PARSING = 'domain_parsing';
private const FIXED_SUBDOMAINS = ['my', 'in', 'checkout'];
private array $domainConfig;
public function __construct(?array $domainConfig = null)
{
$this->domainConfig = $domainConfig ?? config('domains');
}
/**
* Determine the type of subdomain
*/
public function getSubdomainType(string $subdomain): string
{
// Frühe Validierung: Prüfe reservierte Subdomains aus Konfiguration
$reservedSubdomains = $this->domainConfig['reserved_subdomains'] ?? self::FIXED_SUBDOMAINS;
if (in_array($subdomain, $reservedSubdomains)) {
return match($subdomain) {
'my' => 'crm',
'in' => 'portal',
'checkout' => 'checkout',
default => 'unknown' // Andere reservierte Subdomains sind ungültig
};
}
// Frühe Validierung: Prüfe auf ungültige Zeichen für UserShop-Slugs
if (!preg_match('/^[a-z0-9-]+$/', $subdomain) || strlen($subdomain) < 3) {
return 'unknown';
}
// Check if it's a valid user shop
if ($this->isValidUserShop($subdomain)) {
return 'user-shop';
}
return 'unknown';
}
/**
* Check if a subdomain represents a valid user shop
*/
public function isValidUserShop(string $slug): bool
{
$cacheKey = "user_shop_valid_{$slug}";
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug) {
// Optimierte Query mit allen Validierungen in einem DB-Call
$userShop = UserShop::where('slug', $slug)
->where('active', true)
->whereHas('user', function ($query) {
$query->whereNotNull('payment_shop')
->where('payment_shop', '>', now());
})
->exists();
return $userShop;
});
}
/**
* Get user shop by slug with caching
*/
public function getUserShop(string $slug): ?UserShop
{
$cacheKey = "user_shop_{$slug}";
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug) {
// Optimierte Query mit allen Validierungen in einem DB-Call
return UserShop::where('slug', $slug)
->where('active', true)
->whereHas('user', function ($query) {
$query->whereNotNull('payment_shop')
->where('payment_shop', '>', now());
})
->with('user')
->first();
});
}
/**
* Parse domain from request and determine context
*/
public function parseDomain(string $host): array
{
// Normalisiere den Host (lowercase)
$host = strtolower(trim($host));
$parts = explode('.', $host);
// Handle different TLD scenarios
if (count($parts) < 2) {
\Log::warning('Invalid host format', ['host' => $host]);
return [
'type' => 'invalid',
'domain' => $host,
'subdomain' => null,
'tld' => null,
'host' => $host
];
}
// Extract TLD and domain
$tld = '.' . end($parts);
$domain = $parts[count($parts) - 2];
// Check for subdomain
$subdomain = null;
if (count($parts) > 2) {
$subdomain = $parts[0];
\Log::debug('DomainService: Using extracted subdomain', ['subdomain' => $subdomain, 'host' => $host]);
}
// Determine domain type based on subdomain and host
$type = $this->determineDomainType($host, $subdomain);
return [
'type' => $type,
'domain' => $domain,
'subdomain' => $subdomain,
'tld' => $tld,
'host' => $host,
'default_user_shop' => $this->domainConfig['domains']['shop']['default_user_shop'] ?? null
];
}
/**
* Determine domain type based on full host and subdomain
*/
private function determineDomainType(string $host, ?string $subdomain): string
{
// Check against configured domains
foreach ($this->domainConfig['domains'] as $type => $config) {
if (isset($config['host'])) {
// Handle wildcard user-shop pattern
if ($type === 'user-shop') {
$pattern = str_replace('{subdomain}', '([a-z0-9-]+)', $config['host']);
if (preg_match("/^{$pattern}$/", $host)) {
return 'user-shop';
}
} else {
// Exact match for other domains
if ($host === $config['host']) {
return $type;
}
}
}
}
// Additional check for subdomain-based detection
if ($subdomain) {
$subdomainType = $this->getSubdomainType($subdomain);
if ($subdomainType !== 'unknown') {
return $subdomainType;
}
}
return 'unknown';
}
/**
* Build URL for specific domain type
*/
public function buildUrl(string $type, ?string $path = null, ?string $slug = null): string
{
$protocol = $this->domainConfig['protocol'] ?? 'https://';
$domainConfig = $this->domainConfig['domains'][$type] ?? null;
if (!$domainConfig) {
throw new \InvalidArgumentException("Unknown domain type: {$type}");
}
$host = $domainConfig['host'];
// Handle user-shop wildcard
if ($type === 'user-shop') {
if (!$slug) {
throw new \InvalidArgumentException('Slug required for user-shop URLs');
}
$host = str_replace('{subdomain}', $slug, $host);
}
$url = $protocol . $host;
if ($path) {
$url .= '/' . ltrim($path, '/');
}
return $url;
}
/**
* Get domain configuration
*/
public function getDomainConfiguration(): array
{
return $this->domainConfig;
}
/**
* Clear user shop cache
*/
public function clearUserShopCache(string $slug): void
{
Cache::forget("user_shop_valid_{$slug}");
Cache::forget("user_shop_{$slug}");
}
/**
* Clear all user shop caches
*/
public function clearAllUserShopCaches(): void
{
// In Laravel mit Cache-Tags würde das eleganter funktionieren
// Für jetzt eine einfache Lösung für häufig verwendete Shops
$commonSlugs = ['aloevera']; // Füge häufig verwendete Slugs hinzu
foreach ($commonSlugs as $slug) {
$this->clearUserShopCache($slug);
}
}
/**
* Get default user shop for main domain (fallback)
*/
public function getDefaultUserShop(): ?UserShop
{
$defaultSlug = $this->domainConfig['domains']['shop']['default_user_shop'] ?? 'aloevera';
return $this->getUserShop($defaultSlug);
}
/**
* Validate domain configuration
*/
public function validateConfiguration(): array
{
$errors = [];
// Validate main domains
$requiredDomains = ['main', 'shop', 'crm', 'portal', 'checkout', 'user-shop'];
foreach ($requiredDomains as $domain) {
if (empty($this->domainConfig['domains'][$domain]['host'])) {
$errors[] = "Domain '{$domain}' not configured";
}
}
// Validate protocol
if (empty($this->domainConfig['protocol'])) {
$errors[] = 'Protocol not configured';
}
// Validate reserved subdomains
if (empty($this->domainConfig['reserved_subdomains'])) {
$errors[] = 'Reserved subdomains not configured';
}
// Validate shop default
$defaultShop = $this->domainConfig['domains']['shop']['default_user_shop'] ?? null;
if (!$defaultShop) {
$errors[] = 'Default user shop not configured for shop domain';
}
return $errors;
}
/**
* Check if domain configuration is valid
*/
public function isConfigurationValid(): bool
{
return empty($this->validateConfiguration());
}
}

View file

@ -32,7 +32,7 @@ class HTMLHelper
private static $roles = [
0 => 'Kunde',
0 => 'Berater',
1 => 'VIP',
2 => 'Admin',
3 => 'SuperAdmin',
@ -314,7 +314,7 @@ class HTMLHelper
}
public static function getSalutation($id){
$values = array('mr' => __('MR'), 'ms' => __('MS'));
$values = array('mr' => __('MR'), 'ms' => __('MS'), 'di' => __('DIV'));
$ret = "";
$ret .= '<option value="">'.__('please select').'</option>\n';
foreach ($values as $key => $value){
@ -325,7 +325,7 @@ class HTMLHelper
}
public static function getSalutationLang($id){
$values = array('mr' => __('MR'), 'ms' => __('MS'));
$values = array('mr' => __('MR'), 'ms' => __('MS'), 'di' => __('DIV'));
return (!empty($values[$id]) ? $values[$id] : '');
}

View file

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

View file

@ -0,0 +1,125 @@
<?php
namespace App\Services;
use App\Models\UserBusiness;
/**
* Helper-Klasse für die optimierte Generierung von Next-Level-Badges
*
* Diese Klasse nutzt ausschließlich bereits berechnete und gespeicherte Daten
* aus der UserBusiness-Tabelle, anstatt für jeden User eine neue TreeCalcBot-Instanz
* zu erstellen. Dies führt zu erheblichen Performance-Verbesserungen bei DataTables.
*/
class NextLevelBadgeHelper
{
/**
* Generiert Badge für nächsten Level Qualifikation basierend auf UserBusiness-Daten
*
* @param UserBusiness $userBusiness Bereits gespeicherte Business-Daten
* @return string HTML Badge
*/
public static function generateBadgeFromUserBusiness(UserBusiness $userBusiness): string
{
return self::renderBadge($userBusiness);
}
/**
* Generiert Badge basierend auf BusinessUser-Objekt (für TreeCalcBot-Kompatibilität)
*
* @param object $businessUser Business-User-Objekt mit Level-Daten
* @return string HTML Badge
*/
public static function generateBadgeFromBusinessUser($businessUser): string
{
return self::renderBadge($businessUser);
}
/**
* Zentrale Badge-Rendering-Logik
*
* @param mixed $source UserBusiness Model oder BusinessUser Objekt
* @return string HTML Badge
*/
private static function renderBadge($source): string
{
// Prüfe ob User für den nächsten Level qualifiziert ist (grün)
if (!empty($source->next_qual_user_level)) {
return self::renderQualifiedBadge($source);
}
// Prüfe ob User den Level erreichen könnte, aber noch nicht qualifiziert ist (gelb)
if (!empty($source->next_can_user_level)) {
return self::renderCanReachBadge($source);
}
// Kein nächster Level verfügbar (rot)
return self::renderNoLevelBadge();
}
/**
* Rendert Badge für qualifizierte User (grün)
*/
private static function renderQualifiedBadge($source): string
{
$level = $source->next_qual_user_level;
$ku = formatNumber($source->sales_volume_points_KP_sum ?? 0, 0);
$ku_required = formatNumber($level['qual_kp'] ?? 0, 0);
$tp = formatNumber($source->payline_points_qual_kp ?? 0, 0);
$tp_required = formatNumber($level['qual_pp'] ?? 0, 0);
$levelName = TranslationHelper::transUserLevelName($level['name'] ?? 'Unbekannt');
return '<span class="badge badge-outline-success" title="Qualifiziert für nächsten Level">
<i class="fa fa-check"></i> ' . e($levelName) . '<br/>
<small>KU: ' . $ku . '/' . $ku_required . ' | TP: ' . $tp . '/' . $tp_required . '</small>
</span>';
}
/**
* Rendert Badge für User die den Level erreichen könnten (gelb)
*/
private static function renderCanReachBadge($source): string
{
$level = $source->next_can_user_level;
$ku = formatNumber($source->sales_volume_points_KP_sum ?? 0, 0);
$ku_required = formatNumber($level['qual_kp'] ?? 0, 0);
$tp = formatNumber($source->payline_points_qual_kp ?? 0, 0);
$tp_required = formatNumber($level['qual_pp'] ?? 0, 0);
$levelName = TranslationHelper::transUserLevelName($level['name'] ?? 'Unbekannt');
return '<span class="badge badge-outline-warning-dark" title="Noch nicht qualifiziert">
<i class="fa fa-clock"></i> ' . e($levelName) . '<br/>
<small>KU: ' . $ku . '/' . $ku_required . ' | TP: ' . $tp . '/' . $tp_required . '</small>
</span>';
}
/**
* Rendert Badge wenn kein nächster Level verfügbar ist (rot)
*/
private static function renderNoLevelBadge(): string
{
return '<span class="badge badge-outline-warning" title="Kein nächster Level verfügbar">
<i class="fa fa-times"></i>
</span>';
}
/**
* Fallback-Badge bei Fehlern oder fehlenden Daten
*/
public static function renderErrorBadge(string $message = 'Fehler bei der Berechnung'): string
{
return '<span class="badge badge-outline-danger" title="' . e($message) . '">
<i class="fa fa-exclamation"></i> Fehler
</span>';
}
/**
* Badge für fehlende Daten
*/
public static function renderNoDataBadge(): string
{
return '<span class="badge badge-outline-secondary" title="Keine Daten verfügbar">
<i class="fa fa-question"></i> Keine Daten
</span>';
}
}

View file

@ -7,6 +7,7 @@ use App\Models\Country;
use App\Models\Product;
use App\Models\Setting;
use App\Models\ShippingCountry;
use App\Models\ShoppingInstance;
use App\Models\ShoppingUser;
use App\Services\dbip\MyDBIP;
use App\Services\IPinfo\IPinfo;
@ -46,7 +47,7 @@ class Shop
}
public static function getLangChange()
public static function getLangChange($instance = 'shopping')
{
$ret = [];
$countries = Country::whereActive(true)->whereSwitch(true)->get();
@ -60,11 +61,11 @@ class Shop
$ret[strtolower($country->code)] = $country;
}
}
Shop::getUserShopLang($first_country);
Shop::getUserShopLang($first_country, $instance);
return $ret;
}
public static function getUserShopLang($country = null)
public static function getUserShopLang($country = null, $instance = 'shopping')
{
if (\Session::has('user_shop_lang')) {
if ($user_shop_lang = \Session::get('user_shop_lang')) {
@ -72,21 +73,23 @@ class Shop
}
}
if ($country) {
Shop::initUserShopLang($country);
Shop::initUserShopLang($country, $instance);
return strtolower($country->code);
}
return false;
}
public static function initUserShopLang($country)
//init User Shop Lang for Webshop
public static function initUserShopLang($country, $instance = 'shopping')
{
Yard::instance('shopping')->destroy();
Yard::instance($instance)->destroy();
\Session::put('user_shop_lang', strtolower($country->code));
//init Yard
self::initUserShopYard($country);
self::initUserShopYard($country, $instance);
}
public static function initUserShopYard($country)
//init Yard for user shop Webshop
public static function initUserShopYard($country, $instance = 'shopping')
{
//Lieferadresse im Drittland?
self::$user_tax_free = $country->supply_country ? true : false;
@ -95,8 +98,8 @@ class Shop
self::$shipping_country = $ShippingCountry;
self::$user_country = $country;
Yard::instance('shopping')->setShippingCountryWithPrice($ShippingCountry->id);
Yard::instance('shopping')->setUserPriceInfos(Shop::getShopYardInfo());
Yard::instance($instance)->setShippingCountryWithPrice($ShippingCountry->id);
Yard::instance($instance)->setUserPriceInfos(Shop::getShopYardInfo());
}
@ -126,6 +129,23 @@ class Shop
}
return $shopping_user;
}
//prüfe ob checkout bereits gestartet wurde, und wenn ja, dann lösche die Instanz
public static function deleteCheckoutInstance(){
if(Yard::instance('checkout')->count() > 0){
Yard::instance('checkout')->destroy();
}
if(\Session::has('user_shop_identifier')){
ShoppingInstance::where('identifier', \Session::get('user_shop_identifier'))->delete();
\Session::forget('user_shop_identifier');
}
\Session::forget('user_shop_payment');
\Session::forget('auth_user');
\Session::forget('back_link');
\Session::forget('new_session');
}
public static function checkShoppingCountry($for, $id = null)
{

View file

@ -1,9 +1,10 @@
<?php
namespace App\Services;
use Yard;
use App\User;
use App\Models\ShippingCountry;
use App\User;
use Illuminate\Support\Str;
use Yard;
class UserService
{
@ -12,7 +13,7 @@ class UserService
public static $shipping_free = false;
public static $user_tax_free = false;
public static $user_reverse_charge = false;
public static $instance = 'shopping';
public static function getTransChange(){
@ -24,7 +25,11 @@ class UserService
return $ret;
}
public static function setInstance($instance){
self::$instance = $instance;
}
//init Yard for user order Customer
public static function initCustomerYard($shopping_user, $for){
self::$user_tax_free = false;
if($shopping_user->same_as_billing){
@ -40,15 +45,16 @@ class UserService
$ShippingCountry = ShippingCountry::whereCountryId(self::$shipping_country->id)->first();
self::$shipping_free = $ShippingCountry->shipping ? $ShippingCountry->shipping->free : false;
self::$shipping_free = self::$shipping_free !== null ? self::$shipping_free : false;
Yard::instance('shopping')->setShippingCountryWithPrice($ShippingCountry->id, $for);
Yard::instance('shopping')->setUserPriceInfos(self::getYardInfo());
Yard::instance(self::$instance)->setShippingCountryWithPrice($ShippingCountry->id, $for);
Yard::instance(self::$instance)->setUserPriceInfos(self::getYardInfo());
}
//init Yard for user order Berater
public static function initUserYard(User $user, $shipping_country_id, $for){
self::$shipping_free = false;
self::checkUserTaxShippingCountry($user, $shipping_country_id,);
Yard::instance('shopping')->setShippingCountryWithPrice($shipping_country_id, $for);
Yard::instance('shopping')->setUserPriceInfos(self::getYardInfo());
Yard::instance(self::$instance)->setShippingCountryWithPrice($shipping_country_id, $for);
Yard::instance(self::$instance)->setUserPriceInfos(self::getYardInfo());
}
@ -142,7 +148,7 @@ class UserService
public static function createConfirmationCode() {
$unique = false;
do{
$confirmation_code = str_random(30);
$confirmation_code = Str::random(30);
if(User::where('confirmation_code', '=', $confirmation_code)->count() == 0){
$unique = true;
}

View file

@ -143,11 +143,9 @@ class UserUtil
$user->save();
}
public static function deleteUser(User $user)
public static function deleteUser(User $user, $complete = false)
{
if($user->account){
$user->account->delete();
}
//shop wird gelöscht
if($user->shop){
$subdomain_name = $user->shop->slug.'.mivita.care';
$user->shop->name = "delete".$user->shop->id;
@ -163,19 +161,32 @@ class UserUtil
$kas->action('delete_subdomain', $pra);
}
}
$user->email = "delete".time().mt_rand(1000000, 9999999);
//user soll nicht komplett gelöscht werden
$user->email = "delete-".$user->email;
//password wird gelöscht
$user->password = "delete".time();
$user->confirmed = 0;
$user->confirmation_code = "delete".time();
$user->confirmation_date = null;
$user->confirmation_code_to = null;
$user->confirmation_code_remider = 2;
$user->agreement = null;
// $user->agreement = null;
$user->active = 0;
$user->remember_token = '';
$user->active_date = null;
$user->admin = 0;
$user->deleted_at = now();
$user->pre_deleted_at = now();
//user soll komplett gelöscht werden
if($complete){
$user->email = "delete-".time()."-".rand(1000, 9999);
if($user->account){
$user->account->delete();
}
$user->pre_deleted_at = null;
}
$user->save();
return true;

View file

@ -1,12 +1,13 @@
<?php
namespace App\Services;
use Yard;
use App\Models\Country;
use App\Models\UserHistory;
use Illuminate\Support\Str;
use App\Models\ShippingCountry;
use App\Models\UserHistory;
use App\Models\UserShop;
use Illuminate\Support\Str;
use Request;
use Yard;
class Util
{
@ -15,7 +16,7 @@ class Util
public static function getToken()
{
return hash_hmac('sha256', str_random(40), config('app.key'));
return hash_hmac('sha256', Str::random(40), config('app.key'));
}
public static function uuidToken()
@ -122,10 +123,17 @@ class Util
}
public static function getUserShop(){
if(\Session::has('user_shop')){
if($user_shop = \Session::get('user_shop')){
return $user_shop;
}
$shop = session('user_shop');
if (empty($shop) || !is_object($shop)) {
return null;
}
return $shop;
}
public static function getDefaultUserShop(){
$user = \App\User::find(6);
if($user && $user->shop){
return $user->shop;
}
return false;
}
@ -220,17 +228,6 @@ class Util
return array_merge($p, $b);
}
public static function isCheckout(){
if(\Session::has('isCheckout')){
if(\Session::get('isCheckout') == true){
return true;
}
}
return false;
}
public static function checkUserLandIsNot($user){
if(isset($user->account->country_id)){
@ -243,14 +240,35 @@ class Util
return false;
}
public static function getMyMivitaShopUrl($add_url = ""){
if(\Session::has('user_shop_domain')){
$url = \Session::get('user_shop_domain').$add_url;
if (!str_starts_with($url, 'http')) {
$url = 'https://' . ltrim($url, '/');
}
return $url;
}
//alois sein shop
$user = \App\User::find(6);
if($user && $user->shop){
return config('app.protocol').$user->shop->slug.".".config('app.domain').config('app.tld_care').$add_url;
}
}
public static function getMyMivitaPortalUrl($protocol = true){
$pro = $protocol ? config('app.protocol') : "";
return $pro.config('app.pre_url_portal').config('app.domain').config('app.tld_care');
}
public static function getMyMivitaUrl($protocol = true){
$pro = $protocol ? config('app.protocol') : "";
return $pro.config('app.pre_url_crm').config('app.domain').config('app.tld_care');
}
public static function getUserPaymentFor(){
if(Yard::instance('shopping')->getYardExtra('user_shop_payment')){
return Yard::instance('shopping')->getYardExtra('user_shop_payment');
public static function getUserPaymentFor($instance = 'shopping'){
if(Yard::instance($instance)->getYardExtra('user_shop_payment')){
return Yard::instance($instance)->getYardExtra('user_shop_payment');
}
if(\Session::has('user_shop_payment')){
return \Session::get('user_shop_payment');
@ -268,20 +286,20 @@ class Util
return config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care')."/back/to/shop/".$reference;
}
}
return url("/");
return config('app.protocol').config('app.domain').config('app.tld_care');
}
public static function getUserCardBackUrl($uri){
public static function getUserCardBackUrl($uri, $instance = 'shopping'){
if(\Session::has('user_shop')){
if(\Session::has('user_shop_domain')){
if(\Session::has('back_link')){
return \Session::get('back_link');
}
if(self::getUserPaymentFor() === 3){
if(self::getUserPaymentFor($instance) === 3){
return \Session::get('user_shop_domain')."/user/membership";
}
if(self::getUserPaymentFor() === 2){
if(self::getUserPaymentFor($instance) === 2){
return \Session::get('user_shop_domain')."/user/orders";
}
return \Session::get('user_shop_domain');
@ -290,7 +308,7 @@ class Util
return config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care').$uri;
}
}
return url($uri);
return config('app.protocol').config('app.domain').config('app.tld_care');
}
public static function isMivitaShop(){

View file

@ -21,40 +21,49 @@ class Yard extends Cart
private $shipping_country_id = 0; //default de
private $shipping_is_for;
private $num_comp;
private $ysession;
private $user_tax_free;
private $shipping_free;
private $user_reverse_charge;
private $user_country_id;
private $user_country;
private $shopping_data = [];
private $events;
private $initShippingExtras = false;
public function __construct(SessionManager $session, Dispatcher $events)
{
$this->ysession = $session;
$this->yinstance = sprintf('%s.%s', 'cart', 'shipping_extras');
parent::__construct($session, $events);
}
public function instance($instance = null)
{
parent::instance($instance);
if(!$this->initShippingExtras){
$this->initShippingExtras = true; //erst true, sonst wird es immer wieder aufgerufen
$this->makeShippingExtras($instance);
}
return $this;
}
private function makeShippingExtras($instance){
if($this->getYardExtra('shipping_price')){
$this->shipping_price = (float) ($this->getYardExtra('shipping_price'));
}
if($this->getYardExtra('shipping_price_net')){
$this->shipping_price_net = (float) ($this->getYardExtra('shipping_price_net'));
}
if($this->getYardExtra('shipping_tax_rate')){
$this->shipping_tax_rate = (float) ($this->getYardExtra('shipping_tax_rate'));
}
if($this->getYardExtra('shipping_tax')){
$this->shipping_tax = (float) ($this->getYardExtra('shipping_tax'));
}
if($this->getYardExtra('shipping_country_id')){
$this->shipping_country_id = $this->getYardExtra('shipping_country_id');
}
if($this->getYardExtra('shipping_is_for')){
$this->shipping_is_for = $this->getYardExtra('shipping_is_for');
}
@ -77,33 +86,27 @@ class Yard extends Cart
$this->user_country = $this->getYardExtra('user_country');
}
$this->events = $events;
parent::__construct($session, $events);
if(gettype($this->shipping_country_id) !== 'object' && $this->shipping_country_id == 0){
$shippingCountry = ShippingCountry::first();
if($shippingCountry){
$this->shipping_country_id = $shippingCountry->id;
}
}
if($this->shipping_price == 0){
self::instance('shopping')->setShippingCountryWithPrice($this->shipping_country_id, $this->shipping_is_for);
self::instance($instance)->setShippingCountryWithPrice($this->shipping_country_id, $this->shipping_is_for);
}
}
public static function getTaxRate()
public function getTaxRate()
{
return config('cart.tax');
}
public function putYardExtra($key, $value){
$content = $this->getYContent();
$content->put($key, $value);
$this->ysession->put($this->yinstance, $content);
$this->putShippingExtras($content);
//$this->ysession->put($this->yinstance, $content);
}
public function getYardExtra($key){
@ -114,6 +117,15 @@ class Yard extends Cart
return false;
}
public function getYContent()
{
return $this->getShippingExtras();
/* if (is_null($this->ysession->get($this->yinstance))) {
return new Collection([]);
}
return $this->ysession->get($this->yinstance);*/
}
public function getShippingCountryName(){
$shippingCountry = ShippingCountry::find($this->shipping_country_id);
@ -143,13 +155,7 @@ class Yard extends Cart
}
public function getYContent()
{
if (is_null($this->ysession->get($this->yinstance))) {
return new Collection([]);
}
return $this->ysession->get($this->yinstance);
}
public function reCalculateShippingPrice(){
@ -168,22 +174,23 @@ class Yard extends Cart
}
public function setUserPriceInfos($setUserPriceInfos = [])
public function setUserPriceInfos($user_price_infos = [])
{
$this->shipping_free = isset($setUserPriceInfos['shipping_free']) ? $setUserPriceInfos['shipping_free'] : false;
$this->shipping_free = isset($user_price_infos['shipping_free']) ? $user_price_infos['shipping_free'] : false;
$this->putYardExtra('shipping_free', $this->shipping_free);
$this->user_tax_free = $setUserPriceInfos['user_tax_free'];
$this->user_tax_free = $user_price_infos['user_tax_free'];
$this->putYardExtra('user_tax_free', $this->user_tax_free);
$this->user_reverse_charge = $setUserPriceInfos['user_reverse_charge'];
$this->user_reverse_charge = $user_price_infos['user_reverse_charge'];
$this->putYardExtra('user_reverse_charge', $this->user_reverse_charge);
$this->user_country_id = $setUserPriceInfos['user_country_id'];
$this->user_country_id = $user_price_infos['user_country_id'];
$this->putYardExtra('user_country_id', $this->user_country_id);
$this->user_country = Country::findOrFail($setUserPriceInfos['user_country_id']);
$this->user_country = Country::findOrFail($user_price_infos['user_country_id']);
$this->putYardExtra('user_country', $this->user_country);
}
public function getUserPriceInfos(){
@ -476,9 +483,13 @@ class Yard extends Cart
}
$price = $product->price;
if($set_price === 'with'){
$cartItem = $this->getCartItem($product->id, $product->getLang('name'), 1, $price, ['image' => $image, 'slug' => $product->slug, 'weight' => $product->weight, 'points' => $product->points, 'no_commission' => $product->no_commission, 'show_on' => $product->show_on]);
$price = $product->getPriceWith(false, true, $this->getUserCountry());
}
$cartItem = $this->getCartItem($product->id, $product->getLang('name'), 1, $price, ['image' => $image, 'slug' => $product->slug, 'weight' => $product->weight, 'points' => $product->points]);
if($set_price === 'withTaxFree'){
$cartItem = $this->getCartItem($product->id, $product->getLang('name'), 1, $price, ['image' => $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]);
$price = $product->getPriceWith($this->getUserTaxFree(), false, $this->getUserCountry());
}
$content = $this->getContent();
if ($content->has($cartItem->rowId)){
@ -504,7 +515,7 @@ class Yard extends Cart
public function destroy()
{
$this->ysession->remove($this->yinstance);
// $this->ysession->remove($this->yinstance);
parent::destroy();
}
@ -662,7 +673,7 @@ class Yard extends Cart
'cartInstance' => $this->currentInstance(),
], $eventOptions);
$this->events->dispatch('cart.stored', $eventOptions);
// $this->events->dispatch('cart.stored', $eventOptions);
}
/**

View file

@ -118,6 +118,8 @@ use Laravel\Passport\HasApiTokens;
* @property int|null $pre_sponsor
* @property-read User|null $user_pre_sponsor
* @method static \Illuminate\Database\Eloquent\Builder|User wherePreSponsor($value)
* @property \Illuminate\Support\Carbon|null $pre_deleted_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePreDeletedAt($value)
* @mixin \Eloquent
*/
class User extends Authenticatable
@ -144,6 +146,7 @@ class User extends Authenticatable
protected $casts = [
'settings' => 'array',
'payment_methods' => 'array',
'pre_deleted_at' => 'datetime',
];
@ -221,6 +224,11 @@ class User extends Authenticatable
return $this->hasMany('App\Models\ShoppingUser', 'member_id', 'id');
}
public function userBusiness()
{
return $this->hasMany('App\Models\UserBusiness', 'user_id', 'id');
}
public function getLocale()
{
return $this->lang ? $this->lang : \App::getLocale();

View file

@ -1,6 +1,8 @@
<?php
use App\Services\AboHelper;
use App\Domain\DomainContext;
use App\Services\DomainService;
if (! function_exists('make_old_url')) {
function make_old_url($path)
@ -105,4 +107,84 @@ if (! function_exists('cleanIntegerFromString')) {
function cleanIntegerFromString($value) {
return Util::cleanIntegerFromString($value);
}
}
if (! function_exists('legal_url')) {
/**
* Generate URL for legal pages that should always point to shop domain from checkout
*/
function legal_url($path) {
try {
$context = app(DomainContext::class);
// If we're on checkout domain, redirect legal links to shop domain
if ($context->type === 'checkout') {
$domainService = app(DomainService::class);
return $domainService->buildUrl('main', $path);
}
// For all other domains, use normal URL generation
return url($path);
} catch (Exception $e) {
// Fallback to normal URL if something goes wrong
return url($path);
}
}
}
if (! function_exists('main_asset')) {
/**
* Generate an asset URL using the main domain to avoid CORS issues.
* This ensures all assets are loaded from the main domain regardless of subdomain.
*/
function main_asset($path)
{
// Entferne führende Slashes
$path = ltrim($path, '/');
// Baue die Hauptdomain-URL
$protocol = config('app.protocol', 'https://');
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $protocol . $domain . $tld . '/' . $path;
}
}
if (! function_exists('cors_asset')) {
/**
* Alias for main_asset for backward compatibility and clarity.
*/
function cors_asset($path)
{
return main_asset($path);
}
}
if (!function_exists('route')) {
/**
* Route auf Hauptdomain generieren
*/
function route($name, $parameters = [], $absolute = true)
{
$url = route($name, $parameters, $absolute);
// Ersetze Subdomain mit Hauptdomain
if (request()->hasHeader('X-Subdomain')) {
$currentHost = request()->getHost();
$url = str_replace($currentHost, config('app.domain').config('app.tld_care'), $url);
}
return $url;
}
}
if (!function_exists('asset_route')) {
/**
* Asset/Storage Route immer auf Hauptdomain
*/
function asset_route($name, $parameters = [])
{
return 'https://' . config('app.domain') . config('app.tld_care') . route($name, $parameters, false);
}
}

View file

@ -1,21 +1,25 @@
<?php return array (
'askedio/laravel5-profanity-filter' =>
'alban/laravel-collective-spatie-html-parser' =>
array (
'aliases' =>
array (
'Form' => 'Alban\\LaravelCollectiveSpatieHtmlParser\\FormFacade',
),
'providers' =>
array (
0 => 'Askedio\\Laravel5ProfanityFilter\\Providers\\ProfanityFilterServiceProvider',
0 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider',
),
),
'barryvdh/laravel-debugbar' =>
array (
'providers' =>
array (
0 => 'Barryvdh\\Debugbar\\ServiceProvider',
),
'aliases' =>
array (
'Debugbar' => 'Barryvdh\\Debugbar\\Facades\\Debugbar',
),
'providers' =>
array (
0 => 'Barryvdh\\Debugbar\\ServiceProvider',
),
),
'barryvdh/laravel-dompdf' =>
array (
@ -43,52 +47,16 @@
0 => 'Cviebrock\\EloquentSluggable\\ServiceProvider',
),
),
'facade/ignition' =>
array (
'providers' =>
array (
0 => 'Facade\\Ignition\\IgnitionServiceProvider',
),
'aliases' =>
array (
'Flare' => 'Facade\\Ignition\\Facades\\Flare',
),
),
'fideloper/proxy' =>
array (
'providers' =>
array (
0 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
),
),
'fruitcake/laravel-cors' =>
array (
'providers' =>
array (
0 => 'Fruitcake\\Cors\\CorsServiceProvider',
),
),
'intervention/image' =>
array (
'aliases' =>
array (
'Image' => 'Intervention\\Image\\Facades\\Image',
),
'providers' =>
array (
0 => 'Intervention\\Image\\ImageServiceProvider',
),
),
'jenssegers/date' =>
array (
'providers' =>
array (
0 => 'Jenssegers\\Date\\DateServiceProvider',
),
'aliases' =>
array (
'Date' => 'Jenssegers\\Date\\Date',
),
'providers' =>
array (
0 => 'Jenssegers\\Date\\DateServiceProvider',
),
),
'joedixon/laravel-translation' =>
array (
@ -144,18 +112,6 @@
0 => 'Laravel\\Ui\\UiServiceProvider',
),
),
'laravelcollective/html' =>
array (
'providers' =>
array (
0 => 'Collective\\Html\\HtmlServiceProvider',
),
'aliases' =>
array (
'Form' => 'Collective\\Html\\FormFacade',
'Html' => 'Collective\\Html\\HtmlFacade',
),
),
'maatwebsite/excel' =>
array (
'aliases' =>
@ -181,22 +137,51 @@
0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
),
),
'reliese/laravel' =>
'nunomaduro/termwind' =>
array (
'providers' =>
array (
0 => 'Reliese\\Coders\\CodersServiceProvider',
0 => 'Termwind\\Laravel\\TermwindServiceProvider',
),
),
'pestphp/pest-plugin-laravel' =>
array (
'providers' =>
array (
0 => 'Pest\\Laravel\\PestServiceProvider',
),
),
'spatie/laravel-html' =>
array (
'aliases' =>
array (
'Html' => 'Spatie\\Html\\Facades\\Html',
),
'providers' =>
array (
0 => 'Spatie\\Html\\HtmlServiceProvider',
),
),
'spatie/laravel-ignition' =>
array (
'aliases' =>
array (
'Flare' => 'Spatie\\LaravelIgnition\\Facades\\Flare',
),
'providers' =>
array (
0 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider',
),
),
'yajra/laravel-datatables-oracle' =>
array (
'providers' =>
array (
0 => 'Yajra\\DataTables\\DataTablesServiceProvider',
),
'aliases' =>
array (
'DataTables' => 'Yajra\\DataTables\\Facades\\DataTables',
),
'providers' =>
array (
0 => 'Yajra\\DataTables\\DataTablesServiceProvider',
),
),
);

View file

@ -23,43 +23,39 @@
19 => 'Illuminate\\Translation\\TranslationServiceProvider',
20 => 'Illuminate\\Validation\\ValidationServiceProvider',
21 => 'Illuminate\\View\\ViewServiceProvider',
22 => 'Askedio\\Laravel5ProfanityFilter\\Providers\\ProfanityFilterServiceProvider',
22 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider',
23 => 'Barryvdh\\Debugbar\\ServiceProvider',
24 => 'Barryvdh\\DomPDF\\ServiceProvider',
25 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
26 => 'Cviebrock\\EloquentSluggable\\ServiceProvider',
27 => 'Facade\\Ignition\\IgnitionServiceProvider',
28 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
29 => 'Fruitcake\\Cors\\CorsServiceProvider',
30 => 'Intervention\\Image\\ImageServiceProvider',
31 => 'Jenssegers\\Date\\DateServiceProvider',
32 => 'JoeDixon\\Translation\\TranslationServiceProvider',
33 => 'JoeDixon\\Translation\\TranslationBindingsServiceProvider',
34 => 'Laracasts\\Flash\\FlashServiceProvider',
35 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider',
36 => 'Laravel\\Passport\\PassportServiceProvider',
37 => 'Laravel\\Sail\\SailServiceProvider',
38 => 'Laravel\\Tinker\\TinkerServiceProvider',
39 => 'Laravel\\Ui\\UiServiceProvider',
40 => 'Collective\\Html\\HtmlServiceProvider',
41 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
42 => 'Carbon\\Laravel\\ServiceProvider',
43 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
44 => 'Reliese\\Coders\\CodersServiceProvider',
45 => 'Yajra\\DataTables\\DataTablesServiceProvider',
46 => 'Reliese\\Coders\\CodersServiceProvider',
47 => 'Intervention\\Image\\ImageServiceProvider',
48 => 'Laravel\\Tinker\\TinkerServiceProvider',
49 => 'App\\Providers\\AppServiceProvider',
50 => 'App\\Providers\\AuthServiceProvider',
51 => 'App\\Providers\\EventServiceProvider',
52 => 'App\\Providers\\RouteServiceProvider',
53 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
54 => 'Jenssegers\\Date\\DateServiceProvider',
55 => 'Collective\\Html\\HtmlServiceProvider',
56 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
57 => 'Yajra\\DataTables\\DataTablesServiceProvider',
58 => 'App\\Providers\\YardServiceProvider',
27 => 'Jenssegers\\Date\\DateServiceProvider',
28 => 'JoeDixon\\Translation\\TranslationServiceProvider',
29 => 'JoeDixon\\Translation\\TranslationBindingsServiceProvider',
30 => 'Laracasts\\Flash\\FlashServiceProvider',
31 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider',
32 => 'Laravel\\Passport\\PassportServiceProvider',
33 => 'Laravel\\Sail\\SailServiceProvider',
34 => 'Laravel\\Tinker\\TinkerServiceProvider',
35 => 'Laravel\\Ui\\UiServiceProvider',
36 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
37 => 'Carbon\\Laravel\\ServiceProvider',
38 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
39 => 'Termwind\\Laravel\\TermwindServiceProvider',
40 => 'Pest\\Laravel\\PestServiceProvider',
41 => 'Spatie\\Html\\HtmlServiceProvider',
42 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider',
43 => 'Yajra\\DataTables\\DataTablesServiceProvider',
44 => 'Laravel\\Tinker\\TinkerServiceProvider',
45 => 'App\\Providers\\AppServiceProvider',
46 => 'App\\Providers\\AuthServiceProvider',
47 => 'App\\Providers\\EventServiceProvider',
48 => 'App\\Providers\\DomainServiceProvider',
49 => 'App\\Providers\\RouteServiceProvider',
50 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
51 => 'Jenssegers\\Date\\DateServiceProvider',
52 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
53 => 'Yajra\\DataTables\\DataTablesServiceProvider',
54 => 'App\\Providers\\YardServiceProvider',
),
'eager' =>
array (
@ -73,35 +69,33 @@
7 => 'Illuminate\\Pagination\\PaginationServiceProvider',
8 => 'Illuminate\\Session\\SessionServiceProvider',
9 => 'Illuminate\\View\\ViewServiceProvider',
10 => 'Askedio\\Laravel5ProfanityFilter\\Providers\\ProfanityFilterServiceProvider',
10 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider',
11 => 'Barryvdh\\Debugbar\\ServiceProvider',
12 => 'Barryvdh\\DomPDF\\ServiceProvider',
13 => 'Cviebrock\\EloquentSluggable\\ServiceProvider',
14 => 'Facade\\Ignition\\IgnitionServiceProvider',
15 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
16 => 'Fruitcake\\Cors\\CorsServiceProvider',
17 => 'Intervention\\Image\\ImageServiceProvider',
18 => 'Jenssegers\\Date\\DateServiceProvider',
19 => 'JoeDixon\\Translation\\TranslationServiceProvider',
20 => 'Laracasts\\Flash\\FlashServiceProvider',
21 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider',
22 => 'Laravel\\Passport\\PassportServiceProvider',
23 => 'Laravel\\Ui\\UiServiceProvider',
24 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
25 => 'Carbon\\Laravel\\ServiceProvider',
26 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
27 => 'Reliese\\Coders\\CodersServiceProvider',
28 => 'Yajra\\DataTables\\DataTablesServiceProvider',
29 => 'Reliese\\Coders\\CodersServiceProvider',
30 => 'Intervention\\Image\\ImageServiceProvider',
31 => 'App\\Providers\\AppServiceProvider',
32 => 'App\\Providers\\AuthServiceProvider',
33 => 'App\\Providers\\EventServiceProvider',
34 => 'App\\Providers\\RouteServiceProvider',
35 => 'Jenssegers\\Date\\DateServiceProvider',
36 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
37 => 'Yajra\\DataTables\\DataTablesServiceProvider',
38 => 'App\\Providers\\YardServiceProvider',
14 => 'Jenssegers\\Date\\DateServiceProvider',
15 => 'JoeDixon\\Translation\\TranslationServiceProvider',
16 => 'Laracasts\\Flash\\FlashServiceProvider',
17 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider',
18 => 'Laravel\\Passport\\PassportServiceProvider',
19 => 'Laravel\\Ui\\UiServiceProvider',
20 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
21 => 'Carbon\\Laravel\\ServiceProvider',
22 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
23 => 'Termwind\\Laravel\\TermwindServiceProvider',
24 => 'Pest\\Laravel\\PestServiceProvider',
25 => 'Spatie\\Html\\HtmlServiceProvider',
26 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider',
27 => 'Yajra\\DataTables\\DataTablesServiceProvider',
28 => 'App\\Providers\\AppServiceProvider',
29 => 'App\\Providers\\AuthServiceProvider',
30 => 'App\\Providers\\EventServiceProvider',
31 => 'App\\Providers\\DomainServiceProvider',
32 => 'App\\Providers\\RouteServiceProvider',
33 => 'Jenssegers\\Date\\DateServiceProvider',
34 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
35 => 'Yajra\\DataTables\\DataTablesServiceProvider',
36 => 'App\\Providers\\YardServiceProvider',
),
'deferred' =>
array (
@ -112,99 +106,125 @@
'Illuminate\\Contracts\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Bus\\BatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Bus\\DatabaseBatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
'cache' => 'Illuminate\\Cache\\CacheServiceProvider',
'cache.store' => 'Illuminate\\Cache\\CacheServiceProvider',
'cache.psr6' => 'Illuminate\\Cache\\CacheServiceProvider',
'memcached.connector' => 'Illuminate\\Cache\\CacheServiceProvider',
'Illuminate\\Cache\\RateLimiter' => 'Illuminate\\Cache\\CacheServiceProvider',
'command.cache.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.cache.forget' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.clear-compiled' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.auth.resets.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.config.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.config.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\AboutCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\ForgetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ClearCompiledCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Auth\\Console\\ClearResetsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\DbCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.db.prune' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.db.wipe' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.down' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.environment' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.event.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.event.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.event.list' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.key.generate' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.optimize' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.optimize.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.package.discover' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.failed' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.flush' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.forget' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.listen' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.monitor' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.prune-batches' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.prune-failed-jobs' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.restart' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.retry' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.retry-batch' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.work' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.route.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.route.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.route.list' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.schema.dump' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.seed' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\PruneCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\ShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\WipeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\DownCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentDecryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentEncryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Concurrency\\Console\\InvokeSerializedClosureCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\KeyGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\OptimizeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\OptimizeClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\PackageDiscoverCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\PruneStaleTagsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ListFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\FlushFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ForgetFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ListenCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\PruneBatchesCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\PruneFailedJobsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RestartCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RetryCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RetryBatchCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\WorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\DumpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Seeds\\SeedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleFinishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleRunCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleClearCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleTestCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleWorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.storage.link' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.up' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.view.cache' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.view.clear' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.cache.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.cast.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.channel.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.component.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.console.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.controller.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.event.generate' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.event.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.exception.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.factory.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.job.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.listener.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.mail.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.middleware.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.model.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.notification.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.notification.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.observer.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.policy.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.provider.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.failed-table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.queue.batches-table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.request.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.resource.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.rule.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.seeder.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.session.table' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.serve' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.stub.publish' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.test.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.vendor.publish' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleInterruptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\ShowModelCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StorageLinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StorageUnlinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\UpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ApiInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\BroadcastingInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\CacheTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\CastMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ChannelListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ChannelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ClassMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ComponentMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConsoleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Routing\\Console\\ControllerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\DocsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnumMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ExceptionMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Factories\\FactoryMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\InterfaceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\JobMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\JobMiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\LangPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ListenerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\MailMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Routing\\Console\\MiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ModelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\NotificationMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Notifications\\Console\\NotificationTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ObserverMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\PolicyMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ProviderMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\FailedTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\BatchesTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RequestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ResourceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RuleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ScopeMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Seeds\\SeederMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Session\\Console\\SessionTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ServeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StubPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\TestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\TraitMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\VendorPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migration.repository' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migration.creator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate.fresh' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate.install' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate.refresh' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate.reset' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate.rollback' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate.status' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'command.migrate.make' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\MigrateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\FreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\InstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\RefreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\ResetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\RollbackCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\StatusCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\MigrateMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'composer' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'hash' => 'Illuminate\\Hashing\\HashServiceProvider',
'hash.driver' => 'Illuminate\\Hashing\\HashServiceProvider',
@ -212,6 +232,7 @@
'mailer' => 'Illuminate\\Mail\\MailServiceProvider',
'Illuminate\\Mail\\Markdown' => 'Illuminate\\Mail\\MailServiceProvider',
'Illuminate\\Contracts\\Pipeline\\Hub' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
'pipeline' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
'queue' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.connection' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.failer' => 'Illuminate\\Queue\\QueueServiceProvider',
@ -225,15 +246,14 @@
'translation.loader' => 'JoeDixon\\Translation\\TranslationBindingsServiceProvider',
'validator' => 'Illuminate\\Validation\\ValidationServiceProvider',
'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider',
'command.ide-helper.generate' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'command.ide-helper.models' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Illuminate\\Contracts\\Validation\\UncompromisedVerifier' => 'Illuminate\\Validation\\ValidationServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\GeneratorCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\ModelsCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\MetaCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\EloquentCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Laravel\\Sail\\Console\\InstallCommand' => 'Laravel\\Sail\\SailServiceProvider',
'Laravel\\Sail\\Console\\PublishCommand' => 'Laravel\\Sail\\SailServiceProvider',
'command.tinker' => 'Laravel\\Tinker\\TinkerServiceProvider',
'html' => 'Collective\\Html\\HtmlServiceProvider',
'form' => 'Collective\\Html\\HtmlServiceProvider',
'Collective\\Html\\HtmlBuilder' => 'Collective\\Html\\HtmlServiceProvider',
'Collective\\Html\\FormBuilder' => 'Collective\\Html\\HtmlServiceProvider',
),
'when' =>
array (
@ -285,8 +305,5 @@
'Laravel\\Tinker\\TinkerServiceProvider' =>
array (
),
'Collective\\Html\\HtmlServiceProvider' =>
array (
),
),
);

10
clean_script_server.sh Normal file
View file

@ -0,0 +1,10 @@
#!/bin/bash
echo $(date) # Will print the output of date command
/usr/bin/php82 -v
/usr/bin/php82 artisan config:clear
/usr/bin/php82 artisan cache:clear
/usr/bin/php82 artisan view:clear
/usr/bin/php82 artisan optimize:clear
/usr/bin/php82 artisan optimize
# php /path/to/artisan schedule:run 1>> /dev/null 2>&1
# cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

View file

@ -4,51 +4,51 @@
"keywords": ["framework", "laravel"],
"license": "MIT",
"type": "project",
"require": {
"php": "^7.4|^8.0",
"askedio/laravel5-profanity-filter": "*",
"barryvdh/laravel-dompdf": "*",
"cocur/slugify": "4.4.0",
"cviebrock/eloquent-sluggable": "*",
"doctrine/dbal": "*",
"fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"intervention/image": "^2.7",
"jenssegers/date": "*",
"joedixon/laravel-translation": "^2.2",
"laracasts/flash": "*",
"laravel/framework": "^8.12",
"laravel/helpers": "*",
"laravel/passport": "*",
"laravel/tinker": "^2.5",
"laravel/ui": "^3.4",
"laravelcollective/html": "*",
"require": {
"php": "^8.2",
"alban/laravel-collective-spatie-html-parser": "^1.1.9",
"barryvdh/laravel-dompdf": "^2.2",
"cocur/slugify": "^4.5",
"cviebrock/eloquent-sluggable": "^11.0",
"doctrine/dbal": "^3.6.0|^4.0",
"guzzlehttp/guzzle": "^7.4",
"intervention/image": "^3.0",
"jenssegers/date": "^4.0",
"joedixon/laravel-translation": "2.x-dev",
"laracasts/flash": "^3.2",
"laravel/framework": "^11.0",
"laravel/passport": "^12.0",
"laravel/prompts": "^0.1.14",
"laravel/tinker": "^2.9",
"laravel/ui": "^4.2",
"maatwebsite/excel": "^3.1",
"reliese/laravel": "*",
"rguedes/pdfmerger": "^1.0",
"setasign/fpdf": "*",
"setasign/fpdi": "*",
"vitalybaev/google-merchant-feed": "^2.5",
"yajra/laravel-datatables-oracle": "*"
"setasign/fpdf": "^1.8.6",
"setasign/fpdi": "^2.6",
"wearepixel/laravel-google-shopping-feed": "^4.0",
"yajra/laravel-datatables-oracle": "^11.0"
},
"require-dev": {
"facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1",
"laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.2",
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.3.3",
"barryvdh/laravel-debugbar": "*",
"barryvdh/laravel-ide-helper": "*"
},
"conflict": {
"cocur/slugify": "4.5.0"
"spatie/laravel-ignition": "^2.0",
"nunomaduro/collision": "^8.1",
"fakerphp/faker": "^1.23",
"laravel/pint": "^1.0",
"laravel/sail": "^1.26",
"mockery/mockery": "^1.6.2",
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-laravel": "^2.0",
"barryvdh/laravel-debugbar": "^3.13",
"barryvdh/laravel-ide-helper": "^3.0"
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"composer/installers": true
}
},
"extra": {
"laravel": {
@ -71,26 +71,35 @@
"Tests\\": "tests/"
}
},
"scripts": {
"post-update-cmd": [
"php artisan clear-compiled",
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"php artisan ide-helper:models"
],
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
"@php artisan package:discover --ansi",
"@php artisan vendor:publish --force --tag=livewire:assets --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"php artisan ide-helper:models"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
"@php artisan key:generate --ansi",
"@php artisan storage:link --ansi"
],
"format": [
"./vendor/bin/pint"
]
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/bjhijmans/laravel-translation.git"
}
],
"minimum-stability": "dev",
"prefer-stable": true
}

5915
composer.lock generated Executable file → Normal file

File diff suppressed because it is too large Load diff

View file

@ -59,19 +59,21 @@ return [
'protocol' => env('APP_PROTOCOL', 'https://'),
'pre_url_main' => env('APP_URL_MAIN', ''),
'pre_url_crm' => env('APP_URL_CRM', 'my.'),
'pre_url_portal' => env('APP_URL_PORTAL', 'in.'),
'checkout_url' => env('APP_URL_CHECKOUT', 'checkout.'),
'contact_mail' => env('APP_CONTACT_MAIL', 'kontakt@mivita.care'),
'checkout_mail' => env('APP_CHECKOUT_MAIL', 'no-reply@mivita.care'),
'checkout_test_mail' => env('APP_CHECKOUT_TEST_MAIL', 'devtest@mivita.care'),
'info_mail' => env('APP_INFO_MAIL', 'no-reply@mivita.care'),
'priority_mail' => env('APP_PROORITY_MAIL', 'priority@mivita.care'),
'priority_mail' => env('APP_PROORITY_MAIL', 'priority@mivita.care'),
'default_mail' => env('APP_DEFAULT_MAIL', 'info@mivita.care'),
'info_test_mail' => env('APP_INFO_TEST_MAIL', 'devtest@mivita.care'),
'exception_mail' => env('EXCEPTION_MAIL', 'exception@adametz.media'),
'ipinfo' => env('APP_IPINFO', false),
'main_tax' => env('APP_MAIN_TAX', 1.19),
'main_tax_rate' => env('APP_MAIN_TAX_RATE', 19),
'shipping_tax' => env('APP_SHIPPING_TAX', 19),
@ -176,8 +178,8 @@ return [
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Reliese\Coders\CodersServiceProvider::class,
Intervention\Image\ImageServiceProvider::class,
//Reliese\Coders\CodersServiceProvider::class,
//Intervention\Image\ImageServiceProvider::class,
/*
* Package Service Providers...
*/
@ -191,15 +193,13 @@ return [
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\DomainServiceProvider::class,
App\Providers\RouteServiceProvider::class,
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
Jenssegers\Date\DateServiceProvider::class,
Collective\Html\HtmlServiceProvider::class,
Maatwebsite\Excel\ExcelServiceProvider::class,
Yajra\DataTables\DataTablesServiceProvider::class,
App\Providers\YardServiceProvider::class
App\Providers\YardServiceProvider::class,
],
/*
@ -249,9 +249,7 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
//'Input' => Illuminate\Support\Facades\Input::class,
'Form' => Collective\Html\FormFacade::class,
'HTML' => Collective\Html\HtmlFacade::class,
'Image' => Intervention\Image\Facades\Image::class,
//'Image' => Intervention\Image\Facades\Image::class,
'Carbon' => Carbon\Carbon::class,
'Date' => Jenssegers\Date\Date::class,
'HTMLHelper' => App\Services\HTMLHelper::class,

View file

@ -45,6 +45,11 @@ return [
'driver' => 'passport',
'provider' => 'users',
],
'customers' => [ // Neuer Guard für Kunden
'driver' => 'session', // Session-basiert wie der Web-Guard
'provider' => 'customers', // Neuer Provider
],
],
/*
@ -69,6 +74,10 @@ return [
'driver' => 'eloquent',
'model' => App\User::class,
],
'customers' => [
'driver' => 'eloquent',
'model' => App\Models\Customer::class,
],
/* 'api_users' => [
'driver' => 'eloquent',
'model' => App\ApiUser::class,

View file

@ -1,5 +1,7 @@
<?php
use Illuminate\Support\Str;
return [
/*
@ -88,7 +90,7 @@ return [
'prefix' => env(
'CACHE_PREFIX',
str_slug(env('APP_NAME', 'laravel'), '_').'_cache'
Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache'
),
];

26
config/cors.php Normal file
View file

@ -0,0 +1,26 @@
<?php
return [
'paths' => ['api/*', '*'],
'allowed_methods' => ['*'],
'allowed_origins' => [
'https://mivita.care',
'https://*.mivita.care',
],
'allowed_origins_patterns' => [
'/^https:\/\/.*\.mivita\.care$/'
],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];

66
config/domains.php Normal file
View file

@ -0,0 +1,66 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Domain-Konfiguration
|--------------------------------------------------------------------------
|
| Hier werden alle Domains und Subdomains der Anwendung zentral verwaltet.
| Der DomainService und der DomainResolver verwenden diese Konfiguration,
| um den richtigen Kontext für jede Anfrage zu bestimmen.
|
*/
'protocol' => env('APP_PROTOCOL', 'https://'),
'domains' => [
// Die Hauptdomain (z.B. mivita.care)
'main' => [
'host' => env('APP_DOMAIN', 'mivita') . env('APP_TLD_CARE', '.care'),
'type' => 'main',
],
// Die Shop-Domain (z.B. mivita.shop)
'shop' => [
'host' => env('APP_DOMAIN', 'mivita') . env('APP_TLD_SHOP', '.shop'),
'type' => 'main-shop',
'default_user_shop' => 'aloevera', // Fallback-Shop für diese Domain
],
// Das CRM (z.B. my.mivita.care)
'crm' => [
'host' => env('APP_URL_CRM', 'my.') . env('APP_DOMAIN', 'mivita') . env('APP_TLD_CARE', '.care'),
'type' => 'crm',
],
// Das Partner-Portal (z.B. in.mivita.care)
'portal' => [
'host' => env('APP_URL_PORTAL', 'in.') . env('APP_DOMAIN', 'mivita') . env('APP_TLD_CARE', '.care'),
'type' => 'portal',
],
// Die Checkout-Domain (z.B. checkout.mivita.care)
'checkout' => [
'host' => env('APP_URL_CHECKOUT', 'checkout.') . env('APP_DOMAIN', 'mivita') . env('APP_TLD_CARE', '.care'),
'type' => 'checkout',
],
// Die dynamischen User-Shops (z.B. {slug}.mivita.care)
'user-shop' => [
'host' => '{subdomain}.' . env('APP_DOMAIN', 'mivita') . env('APP_TLD_CARE', '.care'),
'type' => 'user-shop',
],
],
// Feste Subdomain-Präfixe, die reserviert sind
'reserved_subdomains' => [
'my',
'in',
'checkout',
'www',
'api',
'mail',
],
];

View file

@ -34,6 +34,11 @@ return [
'channels' => [
'order_controller' => [
'driver' => 'single',
'path' => storage_path('logs/order_controller.log'),
'level' => 'debug',
],
'payone' => [
'driver' => 'single',
'path' => storage_path('logs/payone.log'),
@ -48,7 +53,7 @@ return [
],
'cron' => [
'driver' => 'single',
'path' => storage_path('logs/corn.log'),
'path' => storage_path('logs/cron.log'),
],
'stack' => [
'driver' => 'stack',

View file

@ -48,6 +48,18 @@ return [
/* Replace these words no matter what language. */
'defaults' => [
],
'full_word_check' => [
'aloe',
'aloevera',
'mivita',
'shop',
'onlineshop',
'aloeveramallorca',
'aloemallorca',
'aloeverademallorca',
'babydaloe',
'fuck',
'shit',
'anal',
@ -121,16 +133,5 @@ return [
'mivita',
'shop',
'myaloe',
],
'full_word_check' => [
'aloe',
'aloevera',
'mivita',
'shop',
'onlineshop',
'aloeveramallorca',
'aloemallorca',
'aloeverademallorca',
'babydaloe',
]
];

View file

@ -1,5 +1,7 @@
<?php
use Illuminate\Support\Str;
return [
/*
@ -124,7 +126,7 @@ return [
'cookie' => env(
'SESSION_COOKIE',
str_slug(env('APP_NAME', 'laravel'), '_').'_session'
Str::slug(env('APP_NAME', 'laravel'), '_') . '_session'
),
/*

View file

@ -21,25 +21,28 @@ return [
* To trust one or more specific proxies that connect
* directly to your server, use an array of IP addresses:
*/
# 'proxies' => ['192.168.1.1'],
# 'proxies' => ['192.168.1.1'],
/*
* Or, to trust all proxies that connect
* directly to your server, use a "*"
*/
# 'proxies' => '*',
# 'proxies' => '*',
/*
* Which headers to use to detect proxy related data (For, Host, Proto, Port)
*
* Options include:
*
* - Illuminate\Http\Request::HEADER_X_FORWARDED_ALL (use all x-forwarded-* headers to establish trust)
* - Illuminate\Http\Request::HEADER_FORWARDED (use the FORWARDED header to establish trust)
*
* @link https://symfony.com/doc/current/deployment/proxies.html
* In Laravel 11, instead of using constants, we use an array of trusted headers.
* This replaces the old HEADER_X_FORWARDED_ALL constant.
*/
'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_ALL,
'headers' => [
'for' => 'X-Forwarded-For',
'proto' => 'X-Forwarded-Proto',
'host' => 'X-Forwarded-Host',
'port' => 'X-Forwarded-Port',
'prefix' => 'X-Forwarded-Prefix',
'aws_cloud_front_for' => 'CloudFront-Forwarded-For',
],
];

View file

@ -55,6 +55,7 @@ class CreateUsersTable extends Migration
$table->boolean('test_mode')->default(false);
$table->text('settings')->nullable();
$table->timestamp('pre_deleted_at')->nullable();
$table->timestamps();
$table->softDeletes();

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->string('name')->nullable(); // Optional
$table->string('email')->unique();
$table->unsignedInteger('shopping_user_id')->nullable();
$table->unsignedInteger('member_id')->nullable();
$table->unsignedInteger('number')->index()->nullable();
$table->char('language', 2)->nullable();
$table->char('mode', 4)->nullable();
$table->rememberToken(); // Wichtig für Auth::login()
$table->timestamps();
$table->foreign('shopping_user_id')->references('id')->on('shopping_users')->onDelete('set null');
$table->foreign('member_id')->references('id')->on('members')->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('customers');
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('otp_tokens', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token'); // Speichere hier den HASH des Tokens!
$table->timestamp('expires_at');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('otp_tokens');
}
};

131
routes/web.php → dev/_web.php Executable file → Normal file
View file

@ -11,11 +11,13 @@
|
*/
// Utility routes are loaded directly from the RouteServiceProvider
// They are defined in routes/utility.php
Route::get('storage/images/{from}/{slug}', function($from = null, $slug = null) {
if ($from == 'shop'){
if($image = \App\Models\UserShop::where('filename', $slug)->first()){
$path = storage_path('app/public').'/images/shop'.'/'.$image->filename;
Route::get('storage/images/{from}/{slug}', function ($from = null, $slug = null) {
if ($from == 'shop') {
if ($image = \App\Models\UserShop::where('filename', $slug)->first()) {
$path = storage_path('app/public') . '/images/shop' . '/' . $image->filename;
if (file_exists($path)) {
return Response::file($path);
}
@ -23,10 +25,9 @@ Route::get('storage/images/{from}/{slug}', function($from = null, $slug = null)
}
})->name('storage_images');
Route::get('/product/image/{slug}', function($slug = null)
{
if($image = \App\Models\ProductImage::where('slug', $slug)->first()){
$path = storage_path('app/public').'/images/product'.'/'.$image->product_id.'/'.$image->filename;
Route::get('/product/image/{slug}', function ($slug = null) {
if ($image = \App\Models\ProductImage::where('slug', $slug)->first()) {
$path = storage_path('app/public') . '/images/product' . '/' . $image->product_id . '/' . $image->filename;
if (file_exists($path)) {
return Response::file($path);
}
@ -34,30 +35,27 @@ Route::get('/product/image/{slug}', function($slug = null)
})->name('product_image');
Route::get('/iq/image/{slug}', function($slug = null)
{
if($image = \App\Models\IqImage::where('slug', $slug)->first()){
$path = storage_path('app/public').'/images/iq_images/'.$image->filename;
Route::get('/iq/image/{slug}', function ($slug = null) {
if ($image = \App\Models\IqImage::where('slug', $slug)->first()) {
$path = storage_path('app/public') . '/images/iq_images/' . $image->filename;
if (file_exists($path)) {
return Response::file($path);
}
}
})->name('iq_image');
Route::get('/user_shop/image/{slug}', function($slug = null)
{
if($image = \App\Models\UserShopOnSite::where('slug', $slug)->first()){
$path = storage_path('app/public').'/images/user_shop'.'/'.$image->user_shop_id.'/'.$image->filename;
Route::get('/user_shop/image/{slug}', function ($slug = null) {
if ($image = \App\Models\UserShopOnSite::where('slug', $slug)->first()) {
$path = storage_path('app/public') . '/images/user_shop' . '/' . $image->user_shop_id . '/' . $image->filename;
if (file_exists($path)) {
return Response::file($path);
}
}
})->name('user_shop_image');
Route::get('/shop/product/image/{slug}', function($slug = null)
{
if($image = \App\Models\ProductImage::where('slug', $slug)->first()){
$path = storage_path('app/public').'/images/product'.'/'.$image->product_id.'/'.$image->filename;
Route::get('/shop/product/image/{slug}', function ($slug = null) {
if ($image = \App\Models\ProductImage::where('slug', $slug)->first()) {
$path = storage_path('app/public') . '/images/product' . '/' . $image->product_id . '/' . $image->filename;
if (file_exists($path)) {
return Response::file($path);
}
@ -67,17 +65,22 @@ Route::get('/shop/product/image/{slug}', function($slug = null)
Route::get('translation/{locale}', function ($locale) {
\Session::put('locale', $locale);
\App::setLocale($locale);
if(Auth::check()){
$user = Auth::user();
if (Auth::guard('user')->check()) {
$user = Auth::guard('user')->user();
$user->lang = $locale;
$user->save();
}
if (Auth::guard('customers')->check()) {
$user = Auth::guard('customers')->user();
$user->language = $locale;
$user->save();
}
return redirect()->back();
})->name('translation');
//main site mivita
Route::domain(config('app.pre_url_main').config('app.domain').config('app.tld_care'))->group(function () {
Route::domain(config('app.pre_url_main') . config('app.domain') . config('app.tld_care'))->group(function () {
Route::get('/datenschutz', 'HomeController@legalDataProtected')->name('datenschutz');
Route::get('/impressum', 'HomeController@legalImprint')->name('impressum');
@ -95,7 +98,7 @@ Route::domain(config('app.pre_url_main').config('app.domain').config('app.tld_ca
Route::get('/', 'Web\SiteController@index')->name('/');
/* Route::get('/card/add/{id}/{quantity?}/{product_slug?}', 'Web\CardController@addToCardGet')->name('base.card_add_get');
/* Route::get('/card/add/{id}/{quantity?}/{product_slug?}', 'Web\CardController@addToCardGet')->name('base.card_add_get');
Route::post('/card/add/{id}', 'Web\CardController@addToCardPost')->name('base.card_add_post');
Route::get('/card/show', 'Web\CardController@showCard')->name('base.card_show');
Route::get('/card/checkout', 'Web\CardController@checkoutCard')->name('base.card_checkout');
@ -104,20 +107,19 @@ Route::domain(config('app.pre_url_main').config('app.domain').config('app.tld_ca
Route::get('/card/remove/{rowId}', 'Web\CardController@removeCard')->name('base.card_remove');
Route::get('/card/delete', 'Web\CardController@deleteCard')->name('base.card_delete');*/
Route::get('/{site}/{subsite?}/{product_slug?}', 'Web\SiteController@site')->name('base.site');
});
/* ROUTING FOR CRM my.mivita / CMS*/
Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_care'))->group(function () {
Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'))->group(function () {
Route::get('/cron/jobs/action/{action}/{key}', 'CronController@action')->name('cron_jobs_action');
Route::get('/cron/jobs/run/{key}', 'CronController@runCron')->name('cron_jobs_run');
Auth::routes();
Route::get('/logout', function(){
Route::get('/logout', function () {
Auth::logout();
return Redirect::to('login');
})->name('logout');
@ -130,7 +132,6 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::get('/agb', 'HomeController@legalAGB')->name('agb');
Route::post('/loading/modal', 'HomeController@loadingModal')->name('loading_modal');
Route::get('/', 'HomeController@index')->name('home');
@ -151,7 +152,7 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::post('/homeparty/{token?}/{gid?}', 'Web\HomepartyController@detailStore')->name('homeparty');
Route::group(['middleware' => ['auth']], function() {
Route::group(['middleware' => ['auth']], function () {
Route::get('/user_blocked', 'HomeController@blocked')->name('user_blocked');
Route::get('/wizard-create', 'WizardController@create')->name('wizard_create');
Route::get('/wizard-register', 'WizardController@register')->name('wizard_register');
@ -164,10 +165,9 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::get('/wizard/delete/file/{id}/{relation}', 'WizardController@delete')->name('wizard_delete_file');
Route::get('/storage/file/{id}/{disk}', function($id = null, $disk = null)
{
Route::get('/storage/file/{id}/{disk}', function ($id = null, $disk = null) {
$file = \App\Models\File::findOrFail($id);
$path = Storage::disk($disk)->path($file->dir.$file->filename);
$path = Storage::disk($disk)->path($file->dir . $file->filename);
if (file_exists($path)) {
return Response::file($path);
}
@ -177,17 +177,15 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
});
Route::group(['middleware' => ['auth:user']], function()
{
Route::get('storage/{type?}/{file?}', function($type = null, $file = null)
{
if($type == 'xls'){
Route::group(['middleware' => ['auth:user']], function () {
Route::get('storage/{type?}/{file?}', function ($type = null, $file = null) {
if ($type == 'xls') {
$path = storage_path("app/export/");
$filename = $file.'.xls';
$filename = $file . '.xls';
}
if (file_exists($path.$filename)) {
return Response::download($path.$filename, $filename);
if (file_exists($path . $filename)) {
return Response::download($path . $filename, $filename);
}
})->name('storage');
@ -258,7 +256,7 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::post('/user/abos/update/{view}/{id}', 'User\AboController@update')->name('user_abos_update');
Route::get('/user/abo/datatable/{id}', 'User\AboController@datatable')->name('user_abo_datatable');
//user customers
Route::get('/user/customers', 'User\CustomerController@index')->name('user_customers');
Route::get('/user/customer/detail/{id}', 'User\CustomerController@detail')->name('user_customer_detail');
@ -267,7 +265,7 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::post('/user/customer/edit/{id}', 'User\CustomerController@store')->name('user_customer_edit');
Route::get('/user/customer/datatable', 'User\CustomerController@getCustomers')->name('user_customer_datatable');
//user order & do order
Route::get('/user/orders', 'User\OrderController@index')->name('user_orders');
Route::get('/user/orders/datatable', 'User\OrderController@ordersDatatable')->name('user_orders_datatable');
@ -321,11 +319,9 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::get('/user/downloadcenter', 'User\DownloadController@index')->name('user_downloadcenter');
Route::post('/user/downloadcenter/search', 'User\DownloadController@search')->name('user_downloadcenter_search');
});
Route::group(['middleware' => ['admin']], function()
{
Route::group(['middleware' => ['admin']], function () {
//translate
Route::get('/admin/translate/{model?}', 'TranslationController@index')->name('admin_translate');
Route::post('/admin/translate/update/{model?}/{lang?}', 'TranslationController@update')->name('admin_translate_update');
@ -372,7 +368,7 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::get('/admin/product/category/image/delete{image_id}/{category_id}', 'CategoryController@imageDelete')->name('admin_product_category_image_delete');
Route::get('/admin/product/category/image/attribute/{image_id}/{attr}/{val}', 'CategoryController@imageAttribute')->name('admin_product_category_image_attribute');
//products attributes
Route::get('/admin/product/attributes', 'AttributeController@index')->name('admin_product_attributes');
Route::post('/admin/product/attribute/store', 'AttributeController@store')->name('admin_product_attribute_store');
@ -431,7 +427,7 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::post('/admin/payments/invoice', 'PaymentInvoiceController@index')->name('admin_payments_invoice');
Route::get('/admin/payments/invoice/datatable', 'PaymentInvoiceController@datatable')->name('admin_payments_invoice_datatable');
//payments credit // Gutschriften
Route::get('/admin/payments/credit', 'PaymentCreditController@index')->name('admin_payments_credit');
Route::post('/admin/payments/credit', 'PaymentCreditController@store')->name('admin_payments_credit');
@ -439,25 +435,18 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::post('/admin/payments/credit/create', 'PaymentCreditController@create')->name('admin_payments_credit_create');
Route::get('/admin/payments/credit/delete/{id}/{del?}', 'PaymentCreditController@delete')->name('admin_payments_credit_delete');
//products sales volume
Route::get('/admin/payments/sales/volumes', 'Admin\PaymentSalesController@index')->name('admin_payments_sales_volumes');
Route::post('/admin/payments/sales/volumes/download', 'Admin\PaymentSalesController@download')->name('admin_payments_sales_volumes_download');
Route::get('/admin/payments/sales/volumes/datatable', 'Admin\PaymentSalesController@datatable')->name('admin_payments_sales_volumes_datatable');
//products sales volume
Route::get('/admin/payments/sales/volumes', 'Admin\PaymentSalesController@index')->name('admin_payments_sales_volumes');
Route::post('/admin/payments/sales/volumes/download', 'Admin\PaymentSalesController@download')->name('admin_payments_sales_volumes_download');
Route::get('/admin/payments/sales/volumes/datatable', 'Admin\PaymentSalesController@datatable')->name('admin_payments_sales_volumes_datatable');
Route::get('/admin/payments/taxadvisor', 'PaymentTaxAdvisorController@index')->name('admin_payments_taxadvisor');
Route::post('/admin/payments/taxadvisor/download', 'PaymentTaxAdvisorController@download')->name('admin_payments_taxadvisor_download');
Route::get('/admin/payments/taxadvisor/datatable', 'PaymentTaxAdvisorController@datatable')->name('admin_payments_taxadvisor_datatable');
//business
Route::get('/admin/business/show', 'BusinessController@show')->name('admin_business_show');
Route::get('/admin/business/structure', 'BusinessController@structure')->name('admin_business_structure');
Route::post('/admin/business/structure', 'BusinessController@structure')->name('admin_business_structure');
Route::get('/admin/business/user/detail/{id}', 'BusinessController@userDetail')->name('admin_business_user_detail');
Route::post('/admin/business/user/detail/{id}', 'BusinessController@userStore')->name('admin_business_user_detail');
Route::get('/admin/business/user/datatable', 'BusinessController@userDatatable')->name('admin_business_user_datatable');
// business routes moved to crm.php to avoid duplicates
//payments points // Gutschreiben
Route::get('/admin/business/points', 'BusinessPointsController@index')->name('admin_business_points');
@ -482,11 +471,10 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::post('/admin/downloadcenter/item/store/{obj}', 'Admin\DownloadController@storeItem')->name('admin_downloadcenter_item_store');
Route::get('/admin/downloadcenter/item/delete/{obj}/{id}', 'Admin\DownloadController@deleteItem')->name('admin_downloadcenter_item_delete');
Route::get('/admin/downloadcenter/datatable', 'Admin\DownloadController@datatable')->name('admin_downloadcenter_datatable');
});
//login pages for superadmin
Route::group(['middleware' => ['superadmin']], function() {
Route::group(['middleware' => ['superadmin']], function () {
//leads
Route::get('/admin/users', 'AdminUserController@index')->name('admin_users');
Route::get('/admin/user/edit/{user_id}', 'AdminUserController@edit')->name('admin_user_edit');
@ -518,11 +506,10 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
Route::get('/admin/settings', 'SettingController@index')->name('admin_settings');
Route::post('/admin/setting/store', 'SettingController@store')->name('admin_setting_store');
});
//login pages for sysadmin
Route::group(['middleware' => ['sysadmin']], function() {
Route::group(['middleware' => ['sysadmin']], function () {
Route::get('/sysadmin/tools', 'SyS\SysController@index')->name('sysadmin_tools');
Route::get('/sysadmin/tool/{tool}', 'SyS\SysController@tool')->name('sysadmin_tool');
Route::post('/sysadmin/tool/store/{tool}', 'SyS\SysController@store')->name('sysadmin_tool_store');
@ -535,9 +522,9 @@ Route::domain(config('app.pre_url_crm').config('app.domain').config('app.tld_car
/* ROUTING the checkout.mivita ... */
Route::domain(config('app.checkout_url').config('app.domain').config('app.tld_care'))->group(function () {
Route::domain(config('app.checkout_url') . config('app.domain') . config('app.tld_care'))->group(function () {
Route::group(['middleware' => ['checkout']], function() {
Route::group(['middleware' => ['checkout']], function () {
Route::get('/datenschutz', 'HomeController@legalDataProtected')->name('datenschutz');
Route::get('/impressum', 'HomeController@legalImprint')->name('impressum');
@ -553,13 +540,12 @@ Route::domain(config('app.checkout_url').config('app.domain').config('app.tld_ca
Route::post('/transaction/status/{status?}/{reference?}', 'Web\CheckoutController@transactionStatus')->name('checkout.transaction_status');
Route::get('/transaction/approved/{transactionId}/{reference}', 'Web\CheckoutController@transactionApproved')->name('checkout.transaction_approved');
});
});
$subDomainRouting = function (){
Route::group(['middleware' => ['subdomain']], function() {
$subDomainRouting = function () {
Route::group(['middleware' => ['subdomain']], function () {
Route::get('/datenschutz', 'HomeController@legalDataProtected')->name('datenschutz');
Route::get('/impressum', 'HomeController@legalImprint')->name('impressum');
Route::get('/datenschutz', 'HomeController@legalDataProtected')->name('data_protected');
@ -587,11 +573,10 @@ $subDomainRouting = function (){
Route::get('/{site}/{subsite?}/{product_slug?}', 'Web\SiteController@site')->name('user.site');
Route::post('/change_website_lang', 'Web\SiteController@changeLang')->name('change_website_lang');
});
};
/* ROUTING the SUBDOMAINS user.mivita.care ... */
Route::domain('{subdomain}.'.config('app.domain').config('app.tld_care'))->group($subDomainRouting);
Route::domain('{subdomain}.' . config('app.domain') . config('app.tld_care'))->group($subDomainRouting);
/* ROUTING the mivita.shop ... */
Route::domain(config('app.domain').config('app.tld_shop'))->group($subDomainRouting);
Route::domain(config('app.domain') . config('app.tld_shop'))->group($subDomainRouting);

16
dev/bak/README.md Normal file
View file

@ -0,0 +1,16 @@
# Backup der alten Domain-Routing-Implementierung
Dieses Verzeichnis enthält ein Backup der Dateien, die vor dem Refactoring des Domain- und Subdomain-Handlings am `YYYY-MM-DD` (Datum einfügen) vorhanden waren.
Das Refactoring wurde durchgeführt, um die folgenden Probleme zu beheben:
- **Performance:** Caching von Datenbankabfragen für User-Shops.
- **Wartbarkeit:** Zentralisierung der Domain-Logik in dedizierte Services und Provider.
- **Struktur:** Einführung einer sauberen, domain-basierten Routen-Struktur.
- **Fehlerbehandlung:** Besseres Handling von unbekannten Subdomains (Redirect statt 503-Fehler).
## Gesicherte Dateien:
- `RouteServiceProvider.php.bak`: Der alte Route Service Provider mit der Logik zum Laden aller Routen-Dateien.
- `Subdomain.php.bak`: Die alte Middleware, die die gesamte Subdomain-Logik, Datenbankabfragen und Session-Manipulationen enthielt.
- `routes/`: Ein komplettes Backup des alten `routes`-Verzeichnisses.

View file

@ -0,0 +1,122 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* This namespace is applied to your controller routes.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'App\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
//
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
{
// Utility routes - app-wide
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/utility.php'));
// Main website routes (mivita.care)
Route::middleware('web')
->namespace($this->namespace)
->domain(config('app.pre_url_main') . config('app.domain') . config('app.tld_care'))
->group(base_path('routes/main.php'));
// CRM/CMS routes (my.mivita.care)
Route::middleware('web')
->namespace($this->namespace)
->domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'))
->group(base_path('routes/crm.php'));
// Checkout routes (checkout.mivita.care)
Route::middleware('web')
->namespace($this->namespace)
->domain(config('app.checkout_url') . config('app.domain') . config('app.tld_care'))
->group(base_path('routes/checkout.php'));
// Portal routes (portal.mivita.care)
Route::middleware('web')
->namespace($this->namespace)
->domain(config('app.pre_url_portal') . config('app.domain') . config('app.tld_care'))
->group(base_path('routes/portal.php'));
// Subdomain routes (*.mivita.care)
Route::middleware('web')
->namespace($this->namespace)
->domain('{subdomain}.' . config('app.domain') . config('app.tld_care'))
->group(base_path('routes/subdomain.php'));
// Shop routes (mivita.shop)
Route::middleware('web')
->namespace($this->namespace)
->domain(config('app.domain') . config('app.tld_shop'))
->group(base_path('routes/subdomain.php'));
// Original web.php - mostly empty now but kept for compatibility
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
Route::domain('api.' . config('app.domain') . config('app.tld_care'))
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
//.
/* Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
*/
}
}

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