mivita/tests/Feature/CheckoutVatIdRevalidationTest.php
Kevin ac3bd6e8ea 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>
2026-07-24 13:56:02 +00:00

147 lines
4.9 KiB
PHP

<?php
use App\Models\Country;
use App\Models\Shipping;
use App\Models\ShippingCountry;
use App\Models\UserAccount;
use App\Services\UserService;
use App\Services\VatIdService;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use function Pest\Laravel\mock;
uses(TestCase::class, RefreshDatabase::class);
function makeVatCheckoutUser(array $accountOverrides = []): array
{
$country = Country::create([
'code' => 'AT',
'phone' => '43',
'en' => 'Austria',
'de' => 'Österreich',
'es' => 'Austria',
'fr' => 'Autriche',
'it' => 'Austria',
'ru' => 'Австрия',
'eu_country' => true,
]);
$shipping = Shipping::create(['name' => 'Österreich', 'active' => true]);
$shippingCountry = ShippingCountry::create([
'shipping_id' => $shipping->id,
'country_id' => $country->id,
]);
$account = UserAccount::create(array_merge([
'first_name' => 'Marlene',
'last_name' => 'Mathis',
'country_id' => $country->id,
'tax_identification_number' => 'ATU64282555',
'taxable_sales' => 1,
], $accountOverrides));
// reverse_charge* sind nicht fillable -> explizit setzen
$account->forceFill(array_merge([
'reverse_charge' => 1,
'reverse_charge_valid' => now(),
'reverse_charge_code' => 'AT',
], array_intersect_key($accountOverrides, array_flip(['reverse_charge', 'reverse_charge_valid', 'reverse_charge_code']))))->save();
$user = User::forceCreate([
'account_id' => $account->id,
'email' => 'vat-'.uniqid('', true).'@example.com',
'password' => bcrypt('secret'),
'lang' => 'de',
]);
return [$user->fresh(), $shippingCountry];
}
beforeEach(function () {
UserService::$user_reverse_charge = false;
});
it('keeps tax free status when vies is unavailable', function () {
// VIES_ENABLED=false in phpunit.xml -> CHECK_UNAVAILABLE -> gespeicherter Status gilt
[$user, $shippingCountry] = makeVatCheckoutUser();
$result = UserService::performUserTaxShippingCountry($user, $shippingCountry);
expect($result)->toBeTrue();
expect(UserService::$user_reverse_charge)->toBeTrue();
expect($user->account->fresh()->reverse_charge)->toBeTruthy();
});
it('revokes reverse charge for german vat ids', function () {
[$user, $shippingCountry] = makeVatCheckoutUser([
'tax_identification_number' => 'DE123456789',
]);
$result = UserService::performUserTaxShippingCountry($user, $shippingCountry);
expect($result)->toBeFalse();
expect(UserService::$user_reverse_charge)->toBeFalse();
$account = $user->account->fresh();
expect($account->reverse_charge)->toBeFalsy();
expect($account->reverse_charge_valid)->toBeNull();
});
it('revokes reverse charge for implausible vat ids', function () {
[$user, $shippingCountry] = makeVatCheckoutUser([
'tax_identification_number' => '53618009',
]);
$result = UserService::performUserTaxShippingCountry($user, $shippingCountry);
expect($result)->toBeFalse();
expect($user->account->fresh()->reverse_charge)->toBeFalsy();
});
it('revokes reverse charge when vies reports the id as invalid', function () {
[$user, $shippingCountry] = makeVatCheckoutUser();
mock(VatIdService::class, function ($mock) {
$mock->shouldReceive('qualifiesForReverseCharge')->andReturn(true);
$mock->shouldReceive('checkViesCached')->andReturn(VatIdService::CHECK_INVALID);
});
$result = UserService::performUserTaxShippingCountry($user, $shippingCountry);
expect($result)->toBeFalse();
$account = $user->account->fresh();
expect($account->reverse_charge)->toBeFalsy();
expect($account->reverse_charge_valid)->toBeNull();
// Die USt-ID selbst bleibt erhalten, damit der User sie neu validieren kann
expect($account->tax_identification_number)->toBe('ATU64282555');
});
it('grants tax free checkout when vies confirms the id', function () {
[$user, $shippingCountry] = makeVatCheckoutUser();
mock(VatIdService::class, function ($mock) {
$mock->shouldReceive('qualifiesForReverseCharge')->andReturn(true);
$mock->shouldReceive('checkViesCached')->andReturn(VatIdService::CHECK_VALID);
});
$result = UserService::performUserTaxShippingCountry($user, $shippingCountry);
expect($result)->toBeTrue();
expect(UserService::$user_reverse_charge)->toBeTrue();
expect($user->account->fresh()->reverse_charge)->toBeTruthy();
});
it('does not run vies check when shipping country does not match reverse charge code', function () {
[$user, $shippingCountry] = makeVatCheckoutUser([
'reverse_charge_code' => 'ES',
]);
$result = UserService::performUserTaxShippingCountry($user, $shippingCountry);
expect($result)->toBeFalse();
// Kein Entzug: Der Status bleibt für passende Lieferländer bestehen
expect($user->account->fresh()->reverse_charge)->toBeTruthy();
});