commit 08-2025
This commit is contained in:
parent
9ae662f63e
commit
480fdc65ed
404 changed files with 65310 additions and 2600431 deletions
69
app/Domain/DomainContext.php
Normal file
69
app/Domain/DomainContext.php
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
342
app/Http/Controllers/Admin/ProductsSalesController.php
Normal file
342
app/Http/Controllers/Admin/ProductsSalesController.php
Normal 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");
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
||||
|
|
|
|||
577
app/Http/Controllers/BusinessControllerOptimized.php
Normal file
577
app/Http/Controllers/BusinessControllerOptimized.php
Normal 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
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
]);
|
||||
|
|
|
|||
311
app/Http/Controllers/Portal/AboController.php
Normal file
311
app/Http/Controllers/Portal/AboController.php
Normal 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;
|
||||
}
|
||||
}
|
||||
203
app/Http/Controllers/Portal/Auth/LoginController.php
Executable file
203
app/Http/Controllers/Portal/Auth/LoginController.php
Executable 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');
|
||||
}
|
||||
}
|
||||
99
app/Http/Controllers/Portal/CustomerController.php
Normal file
99
app/Http/Controllers/Portal/CustomerController.php
Normal 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'));
|
||||
}
|
||||
|
||||
}
|
||||
132
app/Http/Controllers/Portal/InController.php
Executable file
132
app/Http/Controllers/Portal/InController.php
Executable 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'));
|
||||
}*/
|
||||
}
|
||||
116
app/Http/Controllers/Portal/OrderController.php
Normal file
116
app/Http/Controllers/Portal/OrderController.php
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
|||
343
app/Http/Controllers/RevenueReportController.php
Normal file
343
app/Http/Controllers/RevenueReportController.php
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -51,7 +51,6 @@ class CustomerController extends Controller
|
|||
'shopping_user' => $shopping_user,
|
||||
'isAdmin' => false,
|
||||
'isView' => 'customer',
|
||||
|
||||
];
|
||||
return view('user.customer.detail', $data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,6 +232,8 @@ class MembershipController extends Controller
|
|||
return back();
|
||||
|
||||
}
|
||||
\Session()->flash('alert-error', __('msg.error_checkbox_not_confirm'));
|
||||
return back();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>€ '.$product->getFormattedPriceWith().'</strong> +<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']));
|
||||
}
|
||||
}
|
||||
|
|
@ -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) . ' €</div>' .
|
||||
'<span class="small no-line-break">E: ' . formatNumber($individual) . ' | S: ' . formatNumber($shop) . ' €</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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'))){
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)){
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'));
|
||||
|
||||
}
|
||||
}
|
||||
146
app/Http/Middleware/DomainResolver.php
Normal file
146
app/Http/Middleware/DomainResolver.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\UserShop;
|
||||
use App\Services\Util;
|
||||
use Closure;
|
||||
use Auth;
|
||||
use Config;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Uses;
|
||||
|
||||
class Subdomain
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$tld = config('app.tld_care');
|
||||
if(!empty($request->route('subdomain'))){
|
||||
//sub.mivita.care
|
||||
$user_shop = UserShop::where('slug', $request->route('subdomain'))->first();
|
||||
$request->route()->forgetParameter('subdomain');
|
||||
Util::setPostRoute('user/');
|
||||
if($user_shop){
|
||||
if(!$user_shop->active){
|
||||
abort(503);
|
||||
}
|
||||
if(!$user_shop->user){
|
||||
abort(503);
|
||||
}
|
||||
if(!$user_shop->user->isActiveShop()){
|
||||
abort(503);
|
||||
}
|
||||
\Session::put('user_shop', $user_shop);
|
||||
\Session::put('user_shop_domain', config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care'));
|
||||
Config::set('app.url', $user_shop->slug.".".config('app.domain').config('app.tld_care'));
|
||||
return $next($request);
|
||||
}
|
||||
}else{
|
||||
//mivita.shop
|
||||
$tld = config('app.tld_shop');
|
||||
$user_shop = UserShop::where('slug', 'aloevera')->first();
|
||||
//$request->route()->forgetParameter('subdomain');
|
||||
Util::setPostRoute('user/');
|
||||
if($user_shop){
|
||||
\Session::put('user_shop', $user_shop);
|
||||
\Session::put('user_shop_domain', config('app.protocol').config('app.domain').config('app.tld_shop'));
|
||||
Config::set('app.url', config('app.domain').config('app.tld_shop'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
return redirect(config('app.url'));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@ class VerifyCsrfToken extends Middleware
|
|||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
'portal/login/verify', // Temporär für OTP-Login
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
43
app/Mail/MailOTPCustomer.php
Normal file
43
app/Mail/MailOTPCustomer.php
Normal 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
74
app/Models/Customer.php
Normal 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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = [];
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
//
|
||||
}
|
||||
|
|
|
|||
85
app/Providers/DomainServiceProvider.php
Normal file
85
app/Providers/DomainServiceProvider.php
Normal 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
151
app/Repositories/DcRepository.php
Normal file
151
app/Repositories/DcRepository.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
797
app/Services/BusinessPlan/BusinessUserItemOptimized.php
Normal file
797
app/Services/BusinessPlan/BusinessUserItemOptimized.php
Normal 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;
|
||||
}
|
||||
}
|
||||
198
app/Services/BusinessPlan/BusinessUserRepository.php
Normal file
198
app/Services/BusinessPlan/BusinessUserRepository.php
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
910
app/Services/BusinessPlan/TreeCalcBotOptimized.php
Normal file
910
app/Services/BusinessPlan/TreeCalcBotOptimized.php
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
183
app/Services/BusinessPlan/TreeHelperOptimized.php
Normal file
183
app/Services/BusinessPlan/TreeHelperOptimized.php
Normal 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 = ' €';
|
||||
}
|
||||
|
||||
$totalFormatted = $type === 'points' ? $total : formatNumber($total);
|
||||
$individualFormatted = $type === 'points' ? $individual : formatNumber($individual);
|
||||
$shopFormatted = $type === 'points' ? $shop : formatNumber($shop);
|
||||
$suffix = $type === 'points' ? '' : ' €';
|
||||
|
||||
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' ? '' : ' €';
|
||||
|
||||
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 .= ' <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 .= ' <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;
|
||||
}
|
||||
}
|
||||
391
app/Services/BusinessPlan/TreeHtmlRenderer.php
Normal file
391
app/Services/BusinessPlan/TreeHtmlRenderer.php
Normal 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(' ', $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) . ' €</strong> | ' .
|
||||
__('team.e') . ': ' . formatNumber($eSum) . ' € | ' .
|
||||
__('team.s') . ': ' . formatNumber($sSum) . ' €';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
296
app/Services/DomainService.php
Normal file
296
app/Services/DomainService.php
Normal 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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] : '');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
125
app/Services/NextLevelBadgeHelper.php
Normal file
125
app/Services/NextLevelBadgeHelper.php
Normal 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>';
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
1.0.0.0,1.0.0.254,AU,Australia,OC,Oceania
|
||||
1.0.0.255,1.0.0.255,ID,Indonesia,AS,Asia
|
||||
1.0.1.0,1.0.3.255,CN,China,AS,Asia
|
||||
1.0.4.0,1.0.7.255,AU,Australia,OC,Oceania
|
||||
1.0.8.0,1.0.15.255,CN,China,AS,Asia
|
||||
1.0.16.0,1.0.31.255,JP,Japan,AS,Asia
|
||||
1.0.32.0,1.0.63.255,CN,China,AS,Asia
|
||||
1.0.64.0,1.0.127.255,JP,Japan,AS,Asia
|
||||
1.0.128.0,1.0.218.40,TH,Thailand,AS,Asia
|
||||
1.0.218.41,1.0.218.41,MY,Malaysia,AS,Asia
|
||||
1.0.218.42,1.0.255.255,TH,Thailand,AS,Asia
|
||||
1.1.0.0,1.1.0.255,CN,China,AS,Asia
|
||||
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue