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>
196 lines
6.4 KiB
PHP
196 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Repositories;
|
|
|
|
use App\Models\PaymentMethod;
|
|
use App\Models\UserAccount;
|
|
use App\Services\UserUtil;
|
|
use App\Services\VatIdService;
|
|
use App\User;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Validator;
|
|
|
|
class UserRepository extends BaseRepository
|
|
{
|
|
public function __construct(User $model)
|
|
{
|
|
$this->model = $model;
|
|
}
|
|
|
|
public function update($data)
|
|
{
|
|
|
|
if ($data['user_id'] === 'new' || $data['user_id'] == 0) {
|
|
$this->model = User::create([
|
|
'email' => $data['email'],
|
|
'password' => env('APP_KEY'),
|
|
]);
|
|
$this->model->payment_methods = PaymentMethod::getDefaultAsArray();
|
|
$this->model->save();
|
|
} else {
|
|
$this->model = $this->getById($data['user_id']);
|
|
}
|
|
|
|
if (! $this->model->account_id) {
|
|
$account = new UserAccount;
|
|
} else {
|
|
$account = $this->model->account;
|
|
}
|
|
|
|
$data['same_as_billing'] = ! isset($data['same_as_billing']) ? 0 : 1;
|
|
$account->fill($data)->save();
|
|
|
|
if (! $this->model->account_id) {
|
|
$this->model->account_id = $account->id;
|
|
$this->model->save();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function create($data)
|
|
{
|
|
|
|
$this->model = User::create([
|
|
'email' => $data['email'],
|
|
'password' => Hash::make($data['password']),
|
|
]);
|
|
|
|
$account = UserAccount::create([
|
|
'm_salutation' => $data['salutation'],
|
|
'm_first_name' => $data['first_name'],
|
|
'm_last_name' => $data['last_name'],
|
|
'salutation' => $data['salutation'],
|
|
'first_name' => $data['first_name'],
|
|
'last_name' => $data['last_name'],
|
|
]);
|
|
|
|
$this->model->account_id = $account->id;
|
|
$this->model->payment_methods = PaymentMethod::getDefaultAsArray();
|
|
$this->model->save();
|
|
|
|
return $this->model;
|
|
}
|
|
|
|
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, $complete);
|
|
}
|
|
|
|
public function reverse_charge_validate($data, $user, $route)
|
|
{
|
|
|
|
if (isset($data['action']) && $data['action'] == 'reverse_charge_validate') {
|
|
$rules = [
|
|
'tax_identification_number' => 'required',
|
|
];
|
|
$validator = Validator::make($data, $rules);
|
|
if ($validator->fails()) {
|
|
$data = [
|
|
'user' => $user,
|
|
];
|
|
|
|
return redirect($route.'#user-vat-validation')->withErrors($validator)->withInput($data);
|
|
}
|
|
$ret = $this->reverse_charge_activate($data, $user);
|
|
if ($ret === 'error' || $ret === 'error_german') {
|
|
$validator = Validator::make($data, []);
|
|
$message = $ret === 'error_german'
|
|
? __('msg.VATID_german_not_allowed')
|
|
: __('msg.VATID_could_not_be_validated');
|
|
$validator->errors()->add('tax_identification_number_validated', $message);
|
|
$data['reverse_charge'] = 0;
|
|
$data = [
|
|
'user' => $user,
|
|
];
|
|
|
|
return redirect($route.'#user-vat-validation')->withErrors($validator)->withInput($data);
|
|
}
|
|
if ($ret === 'valid') {
|
|
\Session()->flash('alert-success', __('msg.VATID_successfully_entered'));
|
|
|
|
return redirect($route.'#user-vat-validation')->withInput($data);
|
|
|
|
return redirect($route.'#user-vat-validation')->withInput($data);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function reverse_charge_delete($data, $user, $route)
|
|
{
|
|
if (isset($data['action']) && $data['action'] == 'reverse_charge_delete') {
|
|
$user->account->tax_identification_number = '';
|
|
$user->account->reverse_charge = 0;
|
|
$user->account->reverse_charge_code = null;
|
|
$user->account->reverse_charge_valid = null;
|
|
$user->account->save();
|
|
$data['tax_identification_number'] = '';
|
|
\Session()->flash('alert-success', __('msg.reverse_charge_procedure_and_VATID_deleted'));
|
|
|
|
return redirect($route.'#user-vat-validation')->withInput($data);
|
|
}
|
|
}
|
|
|
|
public function reverse_charge_activate($data, $user)
|
|
{
|
|
|
|
/* 'AT' => 'AT-Oesterreich',
|
|
'BE' => 'BE-Belgien',
|
|
'BG' => 'BG-Bulgarien',
|
|
'CY' => 'CY-Zypern',
|
|
'CZ' => 'CZ-Tschechische Republik',
|
|
'DE' => 'DE-Deutschland',
|
|
'DK' => 'DK-Dänemark',
|
|
'EE' => 'EE-Estland',
|
|
'EL' => 'EL-Griechenland',
|
|
'ES' => 'ES-Spanien',
|
|
'FI' => 'FI-Finnland',
|
|
'FR' => 'FR-Frankreich ',
|
|
'HR' => 'HR-Kroatien ',
|
|
'HU' => 'HU-Ungarn',
|
|
'IE' => 'IE-Irland',
|
|
'IT' => 'IT-Italien',
|
|
'LT' => 'LT-Litauen',
|
|
'LU' => 'LU-Luxemburg',
|
|
'LV' => 'LV-Lettland',
|
|
'MT' => 'MT-Malta',
|
|
'NL' => 'NL-Niederlande',
|
|
'PL' => 'PL-Polen',
|
|
'PT' => 'PT-Portugal',
|
|
'RO' => 'RO-Rumänien',
|
|
'SE' => 'SE-Schweden',
|
|
'SI' => 'SI-Slowenien',
|
|
'SK' => 'SK-Slowakei',
|
|
'XI' => 'XI-Nordirland', */
|
|
$accountCountryCode = 'DE';
|
|
|
|
if ($user->account->country_id) {
|
|
$accountCountryCode = $user->account->country->code;
|
|
}
|
|
|
|
$vatService = app(VatIdService::class);
|
|
$vatid = $vatService->normalize($data['tax_identification_number']);
|
|
$countryCode = $vatService->extractCountryCode($vatid, $accountCountryCode);
|
|
|
|
if ($vatService->isGermanVatId($vatid) || $countryCode === 'DE') {
|
|
return 'error_german';
|
|
}
|
|
|
|
if ($vatService->checkVies($vatid, $accountCountryCode) === VatIdService::CHECK_VALID) {
|
|
$user->account->tax_identification_number = $data['tax_identification_number'];
|
|
$user->account->reverse_charge = 1;
|
|
$user->account->reverse_charge_code = $countryCode;
|
|
$user->account->reverse_charge_valid = now();
|
|
$user->account->save();
|
|
|
|
return 'valid';
|
|
}
|
|
|
|
return 'error';
|
|
}
|
|
}
|