Statistiken/Abo: Kundenfeedback 2026-07-24 komplett umgesetzt + Steuern: DATEV-Fixes 08+09/2025 (Sammel-Commit Branch-WIP)

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>
This commit is contained in:
Kevin 2026-07-24 13:51:16 +00:00
parent 657d4bd5b7
commit ac3bd6e8ea
136 changed files with 7610 additions and 2688 deletions

View file

@ -2,20 +2,16 @@
namespace App\Repositories;
use Util;
use Request;
use App\User;
use Validator;
use App\Services\UserUtil;
use App\Models\UserAccount;
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 App\Http\Controllers\Api\KasController;
use Validator;
class UserRepository extends BaseRepository
{
public function __construct(User $model)
{
$this->model = $model;
@ -24,7 +20,7 @@ class UserRepository extends BaseRepository
public function update($data)
{
if ($data['user_id'] === "new" || $data['user_id'] == 0) {
if ($data['user_id'] === 'new' || $data['user_id'] == 0) {
$this->model = User::create([
'email' => $data['email'],
'password' => env('APP_KEY'),
@ -35,16 +31,16 @@ class UserRepository extends BaseRepository
$this->model = $this->getById($data['user_id']);
}
if (!$this->model->account_id) {
$account = new UserAccount();
if (! $this->model->account_id) {
$account = new UserAccount;
} else {
$account = $this->model->account;
}
$data['same_as_billing'] = !isset($data['same_as_billing']) ? 0 : 1;
$data['same_as_billing'] = ! isset($data['same_as_billing']) ? 0 : 1;
$account->fill($data)->save();
if (!$this->model->account_id) {
if (! $this->model->account_id) {
$this->model->account_id = $account->id;
$this->model->save();
}
@ -73,7 +69,6 @@ class UserRepository extends BaseRepository
$this->model->payment_methods = PaymentMethod::getDefaultAsArray();
$this->model->save();
return $this->model;
}
@ -91,31 +86,37 @@ class UserRepository extends BaseRepository
{
if (isset($data['action']) && $data['action'] == 'reverse_charge_validate') {
$rules = array(
$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);
return redirect($route.'#user-vat-validation')->withErrors($validator)->withInput($data);
}
$ret = $this->reverse_charge_activate($data, $user);
if ($ret === 'error') {
if ($ret === 'error' || $ret === 'error_german') {
$validator = Validator::make($data, []);
$validator->errors()->add('tax_identification_number_validated', __('msg.VATID_could_not_be_validated'));
$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);
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);
return redirect($route.'#user-vat-validation')->withInput($data);
return redirect($route.'#user-vat-validation')->withInput($data);
}
}
}
@ -125,12 +126,13 @@ class UserRepository extends BaseRepository
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_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);
return redirect($route.'#user-vat-validation')->withInput($data);
}
}
@ -165,42 +167,30 @@ class UserRepository extends BaseRepository
'SI' => 'SI-Slowenien',
'SK' => 'SK-Slowakei',
'XI' => 'XI-Nordirland', */
$countryCode = 'DE';
$accountCountryCode = 'DE';
if ($user->account->country_id) {
$countryCode = $user->account->country->code;
$accountCountryCode = $user->account->country->code;
}
$vatid = str_replace(array(' ', '.', '-', ',', ', '), '', trim($data['tax_identification_number']));
$cc = substr($vatid, 0, 2);
$vatNo = substr($vatid, 2);
$vatService = app(VatIdService::class);
$vatid = $vatService->normalize($data['tax_identification_number']);
$countryCode = $vatService->extractCountryCode($vatid, $accountCountryCode);
$options = [
'cache_wsdl' => WSDL_CACHE_NONE,
'trace' => 1,
'stream_context' => stream_context_create(
[
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
]
]
)
];
if ($vatService->isGermanVatId($vatid) || $countryCode === 'DE') {
return 'error_german';
}
$client = new \SoapClient("https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl", $options);
$result = $client->checkVat(['countryCode' => $countryCode, 'vatNumber' => $vatNo]);
if ($result->valid == true) {
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';
} else {
return 'error';
}
return 'error';
}
}