10.April 2026
This commit is contained in:
parent
a00c42e770
commit
f58c709945
208 changed files with 19280 additions and 2914 deletions
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\Product;
|
||||
use App\Models\Shipping;
|
||||
use App\Models\ShippingCountry;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingOrderItem;
|
||||
use App\Models\ShoppingUser;
|
||||
use App\Models\UserAbo;
|
||||
use App\Models\UserAboOrder;
|
||||
use App\Models\UserShop;
|
||||
use App\Services\AboHelper;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
it('stellt user_abo_items aus der letzten Bestellung wieder her wenn die Tabelle leer ist', function () {
|
||||
$country = Country::create([
|
||||
'code' => 'DE',
|
||||
'phone' => '49',
|
||||
'en' => 'Germany',
|
||||
'de' => 'Deutschland',
|
||||
'es' => 'Alemania',
|
||||
'fr' => 'Allemagne',
|
||||
'it' => 'Germania',
|
||||
'ru' => 'Германия',
|
||||
]);
|
||||
|
||||
$shipping = Shipping::create([
|
||||
'name' => 'Standard',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$shippingCountry = ShippingCountry::create([
|
||||
'shipping_id' => $shipping->id,
|
||||
'country_id' => $country->id,
|
||||
]);
|
||||
|
||||
$shopOwner = User::forceCreate([
|
||||
'email' => 'shop-owner-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$userShop = UserShop::create([
|
||||
'user_id' => $shopOwner->id,
|
||||
'name' => 'TS'.substr(uniqid('', true), 0, 8),
|
||||
'slug' => 'ts-'.uniqid(),
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$consultant = User::forceCreate([
|
||||
'email' => 'consultant-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$customer = User::forceCreate([
|
||||
'email' => 'customer-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$shoppingUser = ShoppingUser::create([
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'billing_country_id' => $country->id,
|
||||
'shipping_country_id' => $country->id,
|
||||
'billing_email' => 'cust-'.uniqid('', true).'@example.com',
|
||||
]);
|
||||
|
||||
$now = now();
|
||||
$productId = (int) DB::table('products')->insertGetId([
|
||||
'name' => 'Test Abo Produkt',
|
||||
'title' => 'Test Abo Produkt',
|
||||
'active' => true,
|
||||
'show_on' => json_encode(['12']),
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
$product = Product::query()->findOrFail($productId);
|
||||
|
||||
$userAbo = UserAbo::create([
|
||||
'user_id' => null,
|
||||
'member_id' => $consultant->id,
|
||||
'shopping_user_id' => $shoppingUser->id,
|
||||
'is_for' => 'ot',
|
||||
'email' => $shoppingUser->billing_email,
|
||||
'payone_userid' => 999001,
|
||||
'clearingtype' => 'cc',
|
||||
'active' => true,
|
||||
'status' => 2,
|
||||
'abo_interval' => 1,
|
||||
]);
|
||||
|
||||
$shoppingOrder = ShoppingOrder::create([
|
||||
'shopping_user_id' => $shoppingUser->id,
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'country_id' => $shippingCountry->id,
|
||||
'user_shop_id' => $userShop->id,
|
||||
'payment_for' => 6,
|
||||
'points' => 10,
|
||||
'is_abo' => true,
|
||||
'paid' => true,
|
||||
'txaction' => 'paid',
|
||||
'mode' => 'test',
|
||||
'total' => 100,
|
||||
'subtotal' => 90,
|
||||
]);
|
||||
|
||||
ShoppingOrderItem::create([
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'row_id' => 'row-'.uniqid(),
|
||||
'product_id' => $product->id,
|
||||
'comp' => 0,
|
||||
'qty' => 1,
|
||||
'price' => 90,
|
||||
'price_net' => 75,
|
||||
'tax_rate' => 19,
|
||||
'tax' => 15,
|
||||
'slug' => $product->slug ?? 'test',
|
||||
]);
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => 2,
|
||||
'paid' => true,
|
||||
]);
|
||||
|
||||
expect($userAbo->user_abo_items()->count())->toBe(0);
|
||||
|
||||
expect(AboHelper::ensureUserAboItemsFromLatestOrder($userAbo))->toBeTrue();
|
||||
|
||||
$userAbo->refresh();
|
||||
|
||||
expect($userAbo->user_abo_items()->count())->toBe(1)
|
||||
->and($userAbo->user_abo_items->first()->product_id)->toBe($product->id);
|
||||
});
|
||||
|
||||
it('gibt true zurück wenn bereits user_abo_items existieren', function () {
|
||||
$country = Country::create([
|
||||
'code' => 'AT',
|
||||
'phone' => '43',
|
||||
'en' => 'Austria',
|
||||
'de' => 'Österreich',
|
||||
'es' => 'Austria',
|
||||
'fr' => 'Autriche',
|
||||
'it' => 'Austria',
|
||||
'ru' => 'Австрия',
|
||||
]);
|
||||
|
||||
$shipping = Shipping::create([
|
||||
'name' => 'Std2',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$shippingCountry = ShippingCountry::create([
|
||||
'shipping_id' => $shipping->id,
|
||||
'country_id' => $country->id,
|
||||
]);
|
||||
|
||||
$shopOwner = User::forceCreate([
|
||||
'email' => 'so-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$userShop = UserShop::create([
|
||||
'user_id' => $shopOwner->id,
|
||||
'name' => 'TS2'.substr(uniqid('', true), 0, 6),
|
||||
'slug' => 'ts2-'.uniqid(),
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$consultant = User::forceCreate([
|
||||
'email' => 'co-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$customer = User::forceCreate([
|
||||
'email' => 'cu-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$shoppingUser = ShoppingUser::create([
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'billing_country_id' => $country->id,
|
||||
'shipping_country_id' => $country->id,
|
||||
'billing_email' => 'x-'.uniqid('', true).'@example.com',
|
||||
]);
|
||||
|
||||
$now = now();
|
||||
$productId = (int) DB::table('products')->insertGetId([
|
||||
'name' => 'P2',
|
||||
'title' => 'P2',
|
||||
'active' => true,
|
||||
'show_on' => json_encode(['12']),
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
$product = Product::query()->findOrFail($productId);
|
||||
|
||||
$userAbo = UserAbo::create([
|
||||
'member_id' => $consultant->id,
|
||||
'shopping_user_id' => $shoppingUser->id,
|
||||
'is_for' => 'ot',
|
||||
'email' => $shoppingUser->billing_email,
|
||||
'payone_userid' => 999002,
|
||||
'clearingtype' => 'cc',
|
||||
'active' => true,
|
||||
'status' => 2,
|
||||
'abo_interval' => 1,
|
||||
]);
|
||||
|
||||
$userAbo->user_abo_items()->create([
|
||||
'product_id' => $product->id,
|
||||
'comp' => 0,
|
||||
'qty' => 1,
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
expect(AboHelper::ensureUserAboItemsFromLatestOrder($userAbo))->toBeTrue();
|
||||
expect($userAbo->user_abo_items()->count())->toBe(1);
|
||||
});
|
||||
53
tests/Unit/Services/AboHelperGetFirstAboDateTest.php
Normal file
53
tests/Unit/Services/AboHelperGetFirstAboDateTest.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
use App\Services\AboHelper;
|
||||
use Carbon\Carbon;
|
||||
|
||||
describe('getFirstAboDate', function () {
|
||||
beforeEach(function () {
|
||||
Carbon::setTestNow(Carbon::parse('2026-03-31 12:00:00', 'Europe/Berlin'));
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Carbon::setTestNow();
|
||||
});
|
||||
|
||||
it('schiebt Liefertag 5 vom 31. März auf Mai, weil April unter 10 Tagen liegt', function () {
|
||||
$first = AboHelper::getFirstAboDate(now(), 5);
|
||||
|
||||
expect($first->format('Y-m-d'))->toBe('2026-05-05');
|
||||
});
|
||||
|
||||
it('behält Liefertag 10 im April, wenn genau 10 Tage Abstand', function () {
|
||||
$first = AboHelper::getFirstAboDate(now(), 10);
|
||||
|
||||
expect($first->format('Y-m-d'))->toBe('2026-04-10');
|
||||
});
|
||||
|
||||
it('schiebt Liefertag 9 auf Mai, wenn der April-Termin nur 9 Tage Abstand hat', function () {
|
||||
// 31. März 2026 -> nächster Kandidat 9. April (9 Tage), muss auf Mai rutschen
|
||||
$first = AboHelper::getFirstAboDate(now(), 9);
|
||||
|
||||
expect($first->format('Y-m-d'))->toBe('2026-05-09');
|
||||
});
|
||||
});
|
||||
|
||||
describe('calendarDaysUntil', function () {
|
||||
it('zählt Kalendertage, nicht 24h-Intervalle bei Tageszeit von now()', function () {
|
||||
Carbon::setTestNow(Carbon::parse('2026-03-31 14:30:00', 'Europe/Berlin'));
|
||||
|
||||
$apr10 = Carbon::parse('2026-04-10')->startOfDay();
|
||||
expect(AboHelper::calendarDaysUntil(now(), $apr10))->toBe(10);
|
||||
|
||||
Carbon::setTestNow();
|
||||
});
|
||||
|
||||
it('liefert für Liefertag 5 (Mai) genug Tage, dass die Warnung unter 20 Tage nicht greift', function () {
|
||||
Carbon::setTestNow(Carbon::parse('2026-03-31 12:00:00', 'Europe/Berlin'));
|
||||
$may5 = AboHelper::getFirstAboDate(now(), 5);
|
||||
|
||||
expect(AboHelper::calendarDaysUntil(now(), $may5))->toBeGreaterThanOrEqual(20);
|
||||
|
||||
Carbon::setTestNow();
|
||||
});
|
||||
});
|
||||
18
tests/Unit/Services/AboHelperSetAboStatusTest.php
Normal file
18
tests/Unit/Services/AboHelperSetAboStatusTest.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Services\AboHelper;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
afterEach(function () {
|
||||
\Mockery::close();
|
||||
});
|
||||
|
||||
it('setAboStatus tut nichts wenn getUserAbo null ist', function () {
|
||||
$order = \Mockery::mock(ShoppingOrder::class);
|
||||
$order->shouldReceive('getUserAbo')->once()->andReturn(null);
|
||||
|
||||
AboHelper::setAboStatus($order, 2, true);
|
||||
});
|
||||
46
tests/Unit/Services/AboOrdersOverviewTest.php
Normal file
46
tests/Unit/Services/AboOrdersOverviewTest.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingPayment;
|
||||
use App\Services\SyS\AboOrdersOverview;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
it('summiert nur erfolgreiche shopping_payments (Cent)', function () {
|
||||
$order = new ShoppingOrder(['total_shipping' => 119.00]);
|
||||
$failed = new ShoppingPayment(['amount' => 10000]);
|
||||
$failed->txaction = 'failed';
|
||||
$paid = new ShoppingPayment(['amount' => 11900]);
|
||||
$paid->txaction = 'paid';
|
||||
$order->setRelation('shopping_payments', collect([$failed, $paid]));
|
||||
|
||||
expect(AboOrdersOverview::actualChargedCentsFromPayments($order))->toBe(11900);
|
||||
});
|
||||
|
||||
it('addiert mehrere erfolgreiche Zahlungen', function () {
|
||||
$order = new ShoppingOrder;
|
||||
$p1 = new ShoppingPayment(['amount' => 5000]);
|
||||
$p1->txaction = 'paid';
|
||||
$p2 = new ShoppingPayment(['amount' => 6900]);
|
||||
$p2->txaction = 'extern_paid';
|
||||
$order->setRelation('shopping_payments', collect([$p1, $p2]));
|
||||
|
||||
expect(AboOrdersOverview::actualChargedCentsFromPayments($order))->toBe(11900);
|
||||
});
|
||||
|
||||
it('liefert null wenn keine erfolgreiche Zahlung existiert', function () {
|
||||
$order = new ShoppingOrder;
|
||||
$failed = new ShoppingPayment(['amount' => 10000]);
|
||||
$failed->txaction = 'failed';
|
||||
$order->setRelation('shopping_payments', collect([$failed]));
|
||||
|
||||
expect(AboOrdersOverview::actualChargedCentsFromPayments($order))->toBeNull();
|
||||
});
|
||||
|
||||
it('liefert null bei leeren Zahlungen', function () {
|
||||
$order = new ShoppingOrder;
|
||||
$order->setRelation('shopping_payments', collect());
|
||||
|
||||
expect(AboOrdersOverview::actualChargedCentsFromPayments($order))->toBeNull();
|
||||
});
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\Incentive;
|
||||
use App\Models\IncentiveNewAbo;
|
||||
use App\Models\IncentiveParticipant;
|
||||
use App\Models\IncentivePointsLog;
|
||||
use App\Models\Shipping;
|
||||
use App\Models\ShippingCountry;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingUser;
|
||||
use App\Models\UserAbo;
|
||||
use App\Models\UserAboOrder;
|
||||
use App\Models\UserSalesVolume;
|
||||
use App\Models\UserShop;
|
||||
use App\Services\Incentive\IncentivePointsLogRepairService;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
it('setzt fehlende incentive_new_abo_id an einem Log per Bestellung', function () {
|
||||
Carbon::setTestNow(Carbon::parse('2026-05-15 10:00:00'));
|
||||
|
||||
$country = Country::create([
|
||||
'code' => 'DE',
|
||||
'phone' => '49',
|
||||
'en' => 'Germany',
|
||||
'de' => 'Deutschland',
|
||||
'es' => 'Alemania',
|
||||
'fr' => 'Allemagne',
|
||||
'it' => 'Germania',
|
||||
'ru' => 'Германия',
|
||||
]);
|
||||
|
||||
$shipping = Shipping::create(['name' => 'S', 'active' => true]);
|
||||
$shippingCountry = ShippingCountry::create(['shipping_id' => $shipping->id, 'country_id' => $country->id]);
|
||||
|
||||
$shopOwner = User::forceCreate([
|
||||
'email' => 'so-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$userShop = UserShop::create([
|
||||
'user_id' => $shopOwner->id,
|
||||
'name' => 'T',
|
||||
'slug' => 't-'.uniqid(),
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$consultant = User::forceCreate([
|
||||
'email' => 'co-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$customer = User::forceCreate([
|
||||
'email' => 'cu-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$shoppingUser = ShoppingUser::create([
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'billing_country_id' => $country->id,
|
||||
'shipping_country_id' => $country->id,
|
||||
'billing_email' => 'e-'.uniqid('', true).'@example.com',
|
||||
]);
|
||||
|
||||
$userAbo = UserAbo::create([
|
||||
'member_id' => $consultant->id,
|
||||
'shopping_user_id' => $shoppingUser->id,
|
||||
'is_for' => 'ot',
|
||||
'email' => $shoppingUser->billing_email,
|
||||
'payone_userid' => 1,
|
||||
'clearingtype' => 'cc',
|
||||
'active' => true,
|
||||
'status' => 2,
|
||||
'abo_interval' => 1,
|
||||
]);
|
||||
|
||||
$incentive = Incentive::factory()->create([
|
||||
'qualification_start' => '2026-04-01',
|
||||
'qualification_end' => '2026-07-31',
|
||||
'calculation_end' => '2026-08-31',
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
$participant = IncentiveParticipant::factory()->create([
|
||||
'incentive_id' => $incentive->id,
|
||||
'user_id' => $consultant->id,
|
||||
]);
|
||||
|
||||
$newAbo = IncentiveNewAbo::create([
|
||||
'participant_id' => $participant->id,
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'activated_at' => Carbon::now()->subMonth(),
|
||||
]);
|
||||
|
||||
$order = ShoppingOrder::create([
|
||||
'shopping_user_id' => $shoppingUser->id,
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'country_id' => $shippingCountry->id,
|
||||
'user_shop_id' => $userShop->id,
|
||||
'payment_for' => 6,
|
||||
'points' => 10,
|
||||
'is_abo' => true,
|
||||
'paid' => true,
|
||||
'txaction' => 'paid',
|
||||
'mode' => 'test',
|
||||
'total' => 100,
|
||||
'subtotal' => 90,
|
||||
]);
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $order->id,
|
||||
'status' => 2,
|
||||
'paid' => true,
|
||||
]);
|
||||
|
||||
$sv = UserSalesVolume::create([
|
||||
'user_id' => $customer->id,
|
||||
'shopping_order_id' => $order->id,
|
||||
'month' => 5,
|
||||
'year' => 2026,
|
||||
'date' => '15.05.2026',
|
||||
'points' => 10,
|
||||
'total_net' => 90,
|
||||
'status_points' => 1,
|
||||
'status' => 2,
|
||||
'message' => 'x',
|
||||
]);
|
||||
|
||||
IncentivePointsLog::create([
|
||||
'participant_id' => $participant->id,
|
||||
'type' => 'abo',
|
||||
'source_type' => UserSalesVolume::class,
|
||||
'source_id' => $sv->id,
|
||||
'source_label' => 'alt',
|
||||
'month' => 5,
|
||||
'year' => 2026,
|
||||
'points_onetime' => 0,
|
||||
'points_accumulated' => 10,
|
||||
'user_sales_volume_id' => $sv->id,
|
||||
'incentive_new_abo_id' => null,
|
||||
'is_storno' => false,
|
||||
]);
|
||||
|
||||
$service = new IncentivePointsLogRepairService;
|
||||
$r = $service->repairForeignKeys($participant->fresh());
|
||||
|
||||
expect($r['abo_fk'])->toBe(1)
|
||||
->and(IncentivePointsLog::first()->incentive_new_abo_id)->toBe($newAbo->id);
|
||||
|
||||
Carbon::setTestNow();
|
||||
});
|
||||
|
||||
it('legt fehlendes Abo-Tracking ohne UserAboOrder manuell an', function () {
|
||||
Carbon::setTestNow(Carbon::parse('2026-05-10 10:00:00'));
|
||||
|
||||
$country = Country::create([
|
||||
'code' => 'AT',
|
||||
'phone' => '43',
|
||||
'en' => 'Austria',
|
||||
'de' => 'Österreich',
|
||||
'es' => 'Austria',
|
||||
'fr' => 'Autriche',
|
||||
'it' => 'Austria',
|
||||
'ru' => 'Австрия',
|
||||
]);
|
||||
|
||||
$shipping = Shipping::create(['name' => 'S2', 'active' => true]);
|
||||
ShippingCountry::create(['shipping_id' => $shipping->id, 'country_id' => $country->id]);
|
||||
|
||||
$consultant = User::forceCreate([
|
||||
'email' => 'co2-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$customer = User::forceCreate([
|
||||
'email' => 'cu2-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$shoppingUser = ShoppingUser::create([
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'billing_country_id' => $country->id,
|
||||
'shipping_country_id' => $country->id,
|
||||
'billing_email' => 'e2-'.uniqid('', true).'@example.com',
|
||||
]);
|
||||
|
||||
$userAbo = UserAbo::create([
|
||||
'member_id' => $consultant->id,
|
||||
'shopping_user_id' => $shoppingUser->id,
|
||||
'is_for' => 'ot',
|
||||
'email' => $shoppingUser->billing_email,
|
||||
'payone_userid' => 2,
|
||||
'clearingtype' => 'cc',
|
||||
'active' => true,
|
||||
'status' => 2,
|
||||
'abo_interval' => 1,
|
||||
]);
|
||||
|
||||
$incentive = Incentive::factory()->create([
|
||||
'qualification_start' => '2026-04-01',
|
||||
'qualification_end' => '2026-07-31',
|
||||
'calculation_end' => '2026-08-31',
|
||||
'status' => 1,
|
||||
'points_abo_onetime' => 400,
|
||||
]);
|
||||
|
||||
$participant = IncentiveParticipant::factory()->create([
|
||||
'incentive_id' => $incentive->id,
|
||||
'user_id' => $consultant->id,
|
||||
]);
|
||||
|
||||
$service = new IncentivePointsLogRepairService;
|
||||
$added = $service->syncMissingTrackingAbos($participant->fresh());
|
||||
expect($added)->toBe(1)
|
||||
->and(IncentiveNewAbo::where('participant_id', $participant->id)->where('user_abo_id', $userAbo->id)->exists())->toBeTrue()
|
||||
->and(IncentivePointsLog::where('participant_id', $participant->id)->where('type', 'abo')->where('source_type', UserAbo::class)->exists())->toBeTrue();
|
||||
|
||||
Carbon::setTestNow();
|
||||
});
|
||||
18
tests/Unit/Services/LocaleGuardTest.php
Normal file
18
tests/Unit/Services/LocaleGuardTest.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
use App\Services\LocaleGuard;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
it('normalizes supported locales', function () {
|
||||
expect(LocaleGuard::normalize('DE'))->toBe('de');
|
||||
expect(LocaleGuard::normalize('en'))->toBe('en');
|
||||
});
|
||||
|
||||
it('returns null for unsupported or invalid locale strings', function () {
|
||||
expect(LocaleGuard::normalize('-1 or 5*5=25 --'))->toBeNull();
|
||||
expect(LocaleGuard::normalize('xx'))->toBeNull();
|
||||
expect(LocaleGuard::normalize(null))->toBeNull();
|
||||
expect(LocaleGuard::normalize(''))->toBeNull();
|
||||
});
|
||||
147
tests/Unit/Services/PaymentAboCallbackCreatesUserAboTest.php
Normal file
147
tests/Unit/Services/PaymentAboCallbackCreatesUserAboTest.php
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Payone-Server-Callback kann vor dem Checkout-Redirect fertig sein: dann muss
|
||||
* Payment::paymentStatusPaidAction trotzdem UserAbo/UserAboOrder anlegen (createNewAbo).
|
||||
*/
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\PaymentTransaction;
|
||||
use App\Models\Shipping;
|
||||
use App\Models\ShippingCountry;
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingPayment;
|
||||
use App\Models\ShoppingUser;
|
||||
use App\Models\UserAbo;
|
||||
use App\Models\UserAboOrder;
|
||||
use App\Models\UserShop;
|
||||
use App\Services\AboHelper;
|
||||
use App\Services\Payment;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
function paymentAboCallbackTestBootstrap(): array
|
||||
{
|
||||
$country = Country::create([
|
||||
'code' => 'DE',
|
||||
'phone' => '49',
|
||||
'en' => 'Germany',
|
||||
'de' => 'Deutschland',
|
||||
'es' => 'Alemania',
|
||||
'fr' => 'Allemagne',
|
||||
'it' => 'Germania',
|
||||
'ru' => 'Германия',
|
||||
]);
|
||||
|
||||
$shipping = Shipping::create([
|
||||
'name' => 'Standard',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$shippingCountry = ShippingCountry::create([
|
||||
'shipping_id' => $shipping->id,
|
||||
'country_id' => $country->id,
|
||||
]);
|
||||
|
||||
$shopOwner = User::forceCreate([
|
||||
'email' => 'shop-owner-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$userShop = UserShop::create([
|
||||
'user_id' => $shopOwner->id,
|
||||
'name' => 'TS'.substr(uniqid('', true), 0, 8),
|
||||
'slug' => 'ts-'.uniqid(),
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$consultant = User::forceCreate([
|
||||
'email' => 'consultant-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$customer = User::forceCreate([
|
||||
'email' => 'customer-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
]);
|
||||
|
||||
$shoppingUser = ShoppingUser::create([
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'billing_country_id' => $country->id,
|
||||
'shipping_country_id' => $country->id,
|
||||
'billing_email' => 'cust-'.uniqid('', true).'@example.com',
|
||||
'is_for' => 'ot',
|
||||
]);
|
||||
|
||||
$shoppingOrder = ShoppingOrder::create([
|
||||
'shopping_user_id' => $shoppingUser->id,
|
||||
'auth_user_id' => $customer->id,
|
||||
'member_id' => $consultant->id,
|
||||
'country_id' => $shippingCountry->id,
|
||||
'user_shop_id' => $userShop->id,
|
||||
'payment_for' => 6,
|
||||
'points' => 10,
|
||||
'is_abo' => true,
|
||||
'abo_interval' => 15,
|
||||
'paid' => false,
|
||||
'txaction' => 'prev',
|
||||
'mode' => 'test',
|
||||
'total' => 100,
|
||||
'subtotal' => 90,
|
||||
]);
|
||||
|
||||
$shoppingPayment = ShoppingPayment::create([
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'reference' => 'ref-'.uniqid(),
|
||||
'amount' => 10000,
|
||||
'currency' => 'EUR',
|
||||
'clearingtype' => 'cc',
|
||||
'abo_interval' => 15,
|
||||
'mode' => 'test',
|
||||
]);
|
||||
|
||||
PaymentTransaction::create([
|
||||
'shopping_payment_id' => $shoppingPayment->id,
|
||||
'request' => 'transaction',
|
||||
'txid' => 123456,
|
||||
'userid' => 987654,
|
||||
'status' => 'PAYONE',
|
||||
'txaction' => 'paid',
|
||||
'mode' => 'test',
|
||||
]);
|
||||
|
||||
return [$shoppingOrder, $shoppingPayment];
|
||||
}
|
||||
|
||||
it('legt UserAbo an wenn Payone-Callback vor Checkout-Erfolgsseite bezahlt (paymentStatusPaidAction)', function () {
|
||||
[$shoppingOrder, $shoppingPayment] = paymentAboCallbackTestBootstrap();
|
||||
|
||||
expect(UserAbo::count())->toBe(0);
|
||||
|
||||
Payment::paymentStatusPaidAction($shoppingOrder, true, $shoppingPayment);
|
||||
|
||||
$shoppingOrder->refresh();
|
||||
expect((bool) $shoppingOrder->paid)->toBeTrue();
|
||||
|
||||
$userAbo = $shoppingOrder->getUserAbo();
|
||||
expect($userAbo)->not->toBeNull();
|
||||
expect($userAbo->status)->toBe(2);
|
||||
expect(UserAboOrder::where('shopping_order_id', $shoppingOrder->id)->count())->toBe(1);
|
||||
});
|
||||
|
||||
it('createNewAbo ist idempotent wenn UserAboOrder bereits existiert', function () {
|
||||
[$shoppingOrder, $shoppingPayment] = paymentAboCallbackTestBootstrap();
|
||||
|
||||
AboHelper::createNewAbo($shoppingPayment);
|
||||
expect(UserAbo::count())->toBe(1);
|
||||
|
||||
AboHelper::createNewAbo($shoppingPayment);
|
||||
expect(UserAbo::count())->toBe(1);
|
||||
});
|
||||
27
tests/Unit/Services/PayoneCallbackTestbenchTest.php
Normal file
27
tests/Unit/Services/PayoneCallbackTestbenchTest.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use App\Models\ShoppingOrder;
|
||||
use App\Models\ShoppingPayment;
|
||||
use App\Services\SyS\PayoneCallbackTestbench;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
it('baut Payone-Callback-Payload mit passendem price und param', function () {
|
||||
$order = new ShoppingOrder;
|
||||
$order->forceFill(['id' => 42]);
|
||||
$payment = ShoppingPayment::make([
|
||||
'amount' => 11_900,
|
||||
'reference' => 'abcd1234efgh5678',
|
||||
'mode' => 'test',
|
||||
'clearingtype' => 'wlt',
|
||||
]);
|
||||
|
||||
$payload = PayoneCallbackTestbench::buildPayoneCallbackPayload($order, $payment, 123456789);
|
||||
|
||||
expect($payload['price'])->toBe('119.00')
|
||||
->and($payload['param'])->toBe('42')
|
||||
->and($payload['txaction'])->toBe('paid')
|
||||
->and($payload['reference'])->toBe('abcd1234efgh5678')
|
||||
->and($payload['key'])->toBe((string) config('payone.defaults.key'));
|
||||
});
|
||||
96
tests/Unit/Services/PayoneTest.php
Normal file
96
tests/Unit/Services/PayoneTest.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
use App\Services\Payone;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
it('parst text/plain Antworten in ein assoziatives Array', function () {
|
||||
$body = "status=OK\nreference=abc123\nerrormessage=\n";
|
||||
$response = new Response(200, ['Content-Type' => 'text/plain;charset=UTF-8'], $body);
|
||||
|
||||
$parsed = Payone::parseResponse($response);
|
||||
|
||||
expect($parsed)->toBeArray()
|
||||
->and($parsed['status'])->toBe('OK')
|
||||
->and($parsed['reference'])->toBe('abc123')
|
||||
->and($parsed['errormessage'])->toBe('');
|
||||
});
|
||||
|
||||
it('parst Werte mit Gleichheitszeichen im Wert', function () {
|
||||
$body = "status=OK\nfoo=bar=baz\n";
|
||||
$response = new Response(200, ['Content-Type' => 'text/plain;charset=UTF-8'], $body);
|
||||
|
||||
$parsed = Payone::parseResponse($response);
|
||||
|
||||
expect($parsed['foo'])->toBe('bar=baz');
|
||||
});
|
||||
|
||||
it('sendRequest liefert das geparste Array bei text/plain;charset=UTF-8', function () {
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'text/plain;charset=UTF-8'], "status=APPROVED\ntxid=999\n"),
|
||||
]);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
|
||||
$result = Payone::sendRequest(['request' => 'authorization'], '', $client);
|
||||
|
||||
expect($result)->toBeArray()
|
||||
->and($result['status'])->toBe('APPROVED')
|
||||
->and($result['txid'])->toBe('999');
|
||||
});
|
||||
|
||||
it('sendRequest wandelt Netzwerkfehler ohne HTTP-Antwort in Fehler 1002 um', function () {
|
||||
Mail::fake();
|
||||
|
||||
$mockRequest = new Request('POST', Payone::PAYONE_SERVER_API_URL);
|
||||
$mock = new MockHandler([
|
||||
new ConnectException('cURL error 56: Recv failure: Connection reset by peer', $mockRequest),
|
||||
]);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
|
||||
$thrown = null;
|
||||
try {
|
||||
Payone::sendRequest(['request' => 'authorization'], '', $client);
|
||||
} catch (HttpException $e) {
|
||||
$thrown = $e;
|
||||
}
|
||||
|
||||
expect($thrown)->toBeInstanceOf(HttpException::class)
|
||||
->and($thrown->getStatusCode())->toBe(403)
|
||||
->and($thrown->getMessage())->toContain('Fehlercode: 1002');
|
||||
});
|
||||
|
||||
it('exponiert die erwartete Post-Gateway-URL', function () {
|
||||
expect(Payone::PAYONE_SERVER_API_URL)->toBe('https://api.pay1.de/post-gateway/');
|
||||
});
|
||||
|
||||
/**
|
||||
* Manuell auf dem Server ausführen, wenn TLS/Outbound geprüft werden soll:
|
||||
* PAYONE_CONNECTIVITY_TEST=1 php artisan test --compact tests/Unit/Services/PayoneTest.php
|
||||
*/
|
||||
it('optional: Erreichbarkeit von api.pay1.de (TLS/Outbound)', function () {
|
||||
if (! env('PAYONE_CONNECTIVITY_TEST')) {
|
||||
$this->markTestSkipped('Setze PAYONE_CONNECTIVITY_TEST=1 für einen echten Netzwerk-Check.');
|
||||
}
|
||||
|
||||
$client = new Client([
|
||||
'timeout' => 15,
|
||||
'connect_timeout' => 10,
|
||||
'http_errors' => false,
|
||||
]);
|
||||
|
||||
$response = $client->get('https://api.pay1.de/', [
|
||||
'http_errors' => false,
|
||||
]);
|
||||
|
||||
expect($response->getStatusCode())->toBeGreaterThanOrEqual(200)
|
||||
->and($response->getStatusCode())->toBeLessThan(600);
|
||||
})->group('payone-connectivity');
|
||||
37
tests/Unit/Services/ProductOrderContextTest.php
Normal file
37
tests/Unit/Services/ProductOrderContextTest.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Product;
|
||||
use App\Services\ProductOrderContext;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
it('erlaubt Kunden-Shop show_on 3 für ot-customer ohne Abo', function () {
|
||||
$product = new Product(['show_on' => ['3']]);
|
||||
|
||||
expect(ProductOrderContext::isProductAllowedInContext($product, false, 'ot-customer'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('lehnt reine Abo show_on 12 für ot-customer ohne Abo ab', function () {
|
||||
$product = new Product(['show_on' => ['12']]);
|
||||
|
||||
expect(ProductOrderContext::isProductAllowedInContext($product, false, 'ot-customer'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('erlaubt Abo-Produkte für abo-ot-customer', function () {
|
||||
$product = new Product(['show_on' => ['12']]);
|
||||
|
||||
expect(ProductOrderContext::isProductAllowedInContext($product, true, 'abo-ot-customer'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('erlaubt Berater-Shop show_on 2 für me ohne Abo', function () {
|
||||
$product = new Product(['show_on' => ['2']]);
|
||||
|
||||
expect(ProductOrderContext::isProductAllowedInContext($product, false, 'me'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('mapped customer webshop wie ot-customer ohne Abo', function () {
|
||||
$product = new Product(['show_on' => ['3']]);
|
||||
|
||||
expect(ProductOrderContext::isProductAllowedInCustomerWebshop($product))->toBeTrue();
|
||||
});
|
||||
22
tests/Unit/Services/UtilCustomerReorderCartUrlTest.php
Normal file
22
tests/Unit/Services/UtilCustomerReorderCartUrlTest.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
use App\Services\Util;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
describe('isShopBaseUrlInvalidForUserCard', function () {
|
||||
it('lehnt leere Werte ab', function () {
|
||||
expect(Util::isShopBaseUrlInvalidForUserCard(null))->toBeTrue();
|
||||
expect(Util::isShopBaseUrlInvalidForUserCard(''))->toBeTrue();
|
||||
});
|
||||
|
||||
it('lehnt Portal-Host ab', function () {
|
||||
$portalHost = config('domains.domains.portal.host');
|
||||
expect(Util::isShopBaseUrlInvalidForUserCard('https://'.$portalHost))->toBeTrue();
|
||||
});
|
||||
|
||||
it('akzeptiert typischen User-Shop-Subdomain', function () {
|
||||
expect(Util::isShopBaseUrlInvalidForUserCard('https://testberater.'.config('app.domain').config('app.tld_care')))->toBeFalse();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue