Feedback-Umsetzung (dev/2026-07-22/feedback-analyse-2026-07-24.md, alle 10 Punkte):
- VIP-Statistik: eigener Berater ('ich', Linie 0) in Neue-Kundenabos- und beiden Top-10-Listen; Top-10-Umsatz sortiert nach abgerechneten Ist-Punkten + Abo-Anzahl-Spalte; Fußnote nicht-payline-wirksame Punkte (SalesVolumePoints::nonPaylinePoints)
- Führungskräfte-Liste: beide Sichten 'direkt' (an nächster FK abgeschnitten, additiv) und 'gesamt' (komplette Downline), Gesamt-Spalte nach Kundenzählweise
- Team-Abos: je Ebene 'abgerechnet (Monat)' und 'bis zum 25. erwartet'; Payline-Karte mit Teamabo-/Kundenabo-Zählern
- Incentive-Live-Ranking: Qualifizierte zuerst nummeriert (fixt auch isWinner rank<=max_winners)
- Porto: Netto-/Brutto-Kennzeichnung (Berater netto, Endkunden inkl. MwSt., portal_view-Flag; auch CRM-/Shop-Warenkorb + Checkout), Yard::shipping_tax-Fix
- Struktur-Ansicht: Abo-Badges 'A' (eigenes Abo) und '(n)' (Kundenabos) je Teampartner, gebatcht
- Abo-Löschung: Softdelete setzt active=0 (UserAbo deleting-Hook) + Bereinigungs-Migration für gelöschte-aber-aktive Abos
- Doku: technische Analyse + Kundinnen-Feedback-Dokument unter dev/2026-07-22/
- Tests: IncentiveRankingTest, TeamStructureAboBadgesTest, YardShippingTaxTest (neu); BackofficeStatisticsListsTest, TeamAboOverviewTest, PaylineForecastServiceTest, AboAdminDeleteTest, AboOneTimeViewTest erweitert
Außerdem enthalten (Branch-WIP vor dem Feedback): DATEV-Abgleich-Fixes 08+09/2025, USt-ID-Live-Validierung sowie restliche unkommittete Stände (u. a. Abo-Pause-Sprachkeys).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
224 lines
8.3 KiB
PHP
224 lines
8.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Web;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Category;
|
|
use App\Models\IqSite;
|
|
use App\Models\Product;
|
|
use App\Models\ProductCategory;
|
|
use App\Services\LocaleGuard;
|
|
use App\Services\Shop;
|
|
use App\Services\UserService;
|
|
use App\Services\Util;
|
|
use Request;
|
|
|
|
class SiteController extends Controller
|
|
{
|
|
/**
|
|
* Templates, die eigene Controller mit eigenen View-Daten haben und daher
|
|
* nicht über die Catch-all-Route ({site}/{subsite?}) gerendert werden dürfen.
|
|
*
|
|
* @var array<int, string>
|
|
*/
|
|
private const PROTECTED_TEMPLATES = [
|
|
'checkout',
|
|
'checkout-error',
|
|
'checkout-final',
|
|
'checkout-is-final',
|
|
'card',
|
|
'kontakt',
|
|
'contact-final',
|
|
'registrierung',
|
|
'registrierung_finish',
|
|
'widerruf',
|
|
'widerruf-final',
|
|
'produkte-show',
|
|
'produkte-item',
|
|
'versandkosten',
|
|
'zahlungsarten',
|
|
];
|
|
|
|
private function isRenderableTemplate(string $template): bool
|
|
{
|
|
return ! str_starts_with($template, '_')
|
|
&& ! in_array($template, self::PROTECTED_TEMPLATES, true)
|
|
&& view()->exists('web.templates.'.$template);
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
$this->setIPInfo();
|
|
$products = ['aloe-vera-gel-99', 'aloe-vera-saft-500-ml', 'aloe-vera-lippenbalsam'];
|
|
// $set_products = ['aloe-vera-cleaner-set', 'aloe-vera-koerper-set', 'aloe-vera-repair-set'];
|
|
$set_products = ['aloe-vera-koerper-set', 'baby-set', 'aloe-vera-gel-set'];
|
|
$data = [
|
|
'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('webshop'),
|
|
'site' => IqSite::find(1),
|
|
'yard_instance' => 'webshop',
|
|
];
|
|
|
|
return view('web.index', $data);
|
|
}
|
|
|
|
public function domainCheck()
|
|
{
|
|
exit('checked');
|
|
}
|
|
|
|
public function changeLang()
|
|
{
|
|
$data = Request::all();
|
|
if (isset($data['change_country_id'])) {
|
|
$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');
|
|
$locale = LocaleGuard::normalize($data['change_locale_id'] ?? null);
|
|
if ($locale !== null) {
|
|
\Session::put('locale', $locale);
|
|
}
|
|
Shop::initUserShopLang($country, 'webshop');
|
|
|
|
return back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function setIPinfo()
|
|
{
|
|
// wurde schon gesetzt //cache
|
|
$country = strtolower(Shop::getIPDatabaseInfo());
|
|
if (\Session::has('user_init_country')) {
|
|
return;
|
|
}
|
|
if (config('app.ipinfo')) {
|
|
$country = strtolower(Shop::getIPDatabaseInfo());
|
|
if ($country === 'de') { // $locale de - init AT
|
|
\Session::put('user_init_country', $country);
|
|
|
|
return;
|
|
}
|
|
if ($country === 'error') { // $locale at - init AT
|
|
$country = 'de';
|
|
}
|
|
} else {
|
|
$country = 'de';
|
|
}
|
|
|
|
// $locale = strtolower(\App::getLocale());
|
|
// ist default
|
|
|
|
// sprache
|
|
if (array_key_exists($country, UserService::getTransChange())) {
|
|
\Session::put('user_init_country', $country);
|
|
\Session::put('locale', $country);
|
|
\App::setLocale($country);
|
|
} else {
|
|
// default EN
|
|
\Session::put('user_init_country', 'de');
|
|
\Session::put('locale', 'de');
|
|
\App::setLocale('de');
|
|
}
|
|
|
|
// bestelland / versandland
|
|
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('/');
|
|
}
|
|
|
|
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('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);
|
|
}
|
|
}
|
|
if ($site == 'produkte') {
|
|
if ($subsite || $subsite !== 'alle-produkte') {
|
|
$category = Category::where('slug', $subsite)->where('active', true)->first();
|
|
if ($category) {
|
|
$headline_image = false;
|
|
if ($category->headline_image_id && $category->iq_image && $category->iq_image->active) {
|
|
$headline_image = $category->iq_image;
|
|
}
|
|
|
|
$product_categories = ProductCategory::where('category_id', $category->id)->whereHas('product', function ($query) {
|
|
$query->where('active', true)->whereJsonContains('show_on', '1');
|
|
})->orderBy('pos', 'DESC')->get();
|
|
|
|
$data = [
|
|
'user_shop' => Util::getUserShop(),
|
|
'mylangs' => Shop::getLangChange('webshop'),
|
|
'subsite' => $subsite,
|
|
'categories' => Category::where('active', true)->orderBy('pos', 'DESC')->get(),
|
|
'products' => false,
|
|
'product_categories' => $product_categories,
|
|
'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);
|
|
}
|
|
}
|
|
dd($subsite);
|
|
$data = [
|
|
'user_shop' => Util::getUserShop(),
|
|
'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(),
|
|
'product_categories' => false,
|
|
'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('webshop'),
|
|
'yard_instance' => 'webshop',
|
|
];
|
|
if ($subsite) {
|
|
if (! $this->isRenderableTemplate($subsite)) {
|
|
abort(404);
|
|
}
|
|
|
|
return view('web.templates.'.$subsite, $data);
|
|
}
|
|
if (! $this->isRenderableTemplate($site)) {
|
|
abort(404);
|
|
}
|
|
|
|
return view('web.templates.'.$site, $data);
|
|
}
|
|
}
|