mivita/tests/Feature/CreateAboReplacementOrderTest.php
Kevin 9b7665c59a Abo-Wartungs-Commands & Ausfuehrungs-Reminder aktivieren
Neue Artisan-Commands CreateAboReplacementOrder (Ersatzbestellung fuer ein
Abo anlegen) und RepairAboOrderUserAssignment (fehlerhafte User-Zuordnung
von Abo-Bestellungen korrigieren), jeweils mit Feature-Tests.
Ausserdem den abo:send-execution-reminder-Schedule (06:00) aktiviert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:37:39 +00:00

295 lines
9.3 KiB
PHP

<?php
use App\Models\Country;
use App\Models\PaymentTransaction;
use App\Models\Shipping;
use App\Models\ShippingCountry;
use App\Models\ShippingPrice;
use App\Models\ShoppingOrder;
use App\Models\ShoppingOrderItem;
use App\Models\ShoppingPayment;
use App\Models\ShoppingUser;
use App\Models\UserAbo;
use App\Models\UserAboOrder;
use App\Models\UserAccount;
use App\Models\UserInvoice;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
function createAboReplacementOrderSeed(): 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,
]);
ShippingPrice::create([
'shipping_id' => $shipping->id,
'price' => 12.90,
'price_comp' => 37.00,
'tax_rate' => 19.00,
'weight_from' => 1,
'weight_to' => 20000,
]);
$account = UserAccount::create([
'first_name' => 'Marlene',
'last_name' => 'Mathis',
'address' => 'Oberdorferstrasse 11b',
'zipcode' => '6850',
'city' => 'Dornbirn',
'country_id' => $country->id,
'same_as_billing' => false,
'shipping_firstname' => 'Marlene',
'shipping_lastname' => 'Mathis',
'shipping_address' => 'Oberdorferstrasse 11b',
'shipping_zipcode' => '6850',
'shipping_city' => 'Dornbirn',
'shipping_country_id' => $country->id,
'reverse_charge' => true,
'reverse_charge_valid' => now(),
'reverse_charge_code' => 'AT',
'tax_identification_number' => 'ATU64282555',
'taxable_sales' => 1,
]);
$user = User::forceCreate([
'account_id' => $account->id,
'email' => 'marlene@example.com',
'password' => bcrypt('secret'),
'lang' => 'de',
]);
$shoppingUser = ShoppingUser::create([
'auth_user_id' => $user->id,
'billing_firstname' => 'Marlene',
'billing_lastname' => 'Mathis',
'billing_address' => 'Oberdorferstrasse 11b',
'billing_zipcode' => '6850',
'billing_city' => 'Dornbirn',
'billing_country_id' => $country->id,
'billing_email' => $user->email,
'same_as_billing' => false,
'shipping_firstname' => 'Marlene',
'shipping_lastname' => 'Mathis',
'shipping_address' => 'Oberdorferstrasse 11b',
'shipping_zipcode' => '6850',
'shipping_city' => 'Dornbirn',
'shipping_country_id' => $country->id,
'is_for' => 'me',
'is_from' => 'user_order',
'mode' => 'live',
]);
$userAbo = UserAbo::create([
'user_id' => $user->id,
'shopping_user_id' => $shoppingUser->id,
'is_for' => 'me',
'email' => $user->email,
'payone_userid' => 123456,
'clearingtype' => 'cc',
'active' => true,
'status' => 2,
'abo_interval' => 5,
'next_date' => now()->addDays(3),
]);
$productA = makeProduct(['2'], 119, 19);
$productB = makeProduct(['2'], 10.70, 7);
$sourceOrder = ShoppingOrder::create([
'shopping_user_id' => $shoppingUser->id,
'auth_user_id' => $user->id,
'country_id' => $shippingCountry->id,
'language' => 'de',
'user_shop_id' => 1,
'payment_for' => 3,
'total' => 248.70,
'subtotal' => 210.00,
'shipping' => 39.90,
'shipping_net' => 33.53,
'subtotal_ws' => 243.53,
'tax' => 45.07,
'tax_split' => ['19' => '44.37', '7' => '0.70'],
'net_split' => ['19' => '233.53', '7' => '10.00'],
'total_shipping' => 288.60,
'points' => 20,
'weight' => 18100,
'paid' => true,
'shipped' => 10,
'is_abo' => true,
'abo_interval' => 5,
'txaction' => 'cancelled',
'mode' => 'live',
]);
ShoppingOrderItem::create([
'shopping_order_id' => $sourceOrder->id,
'row_id' => 'row-a',
'product_id' => $productA->id,
'qty' => 2,
'price' => 119.00,
'price_net' => 100.00,
'tax_rate' => 19.00,
'tax' => 19.00,
'price_vk_net' => 100.00,
'discount' => 30.00,
'points' => 10,
'slug' => $productA->slug,
]);
ShoppingOrderItem::create([
'shopping_order_id' => $sourceOrder->id,
'row_id' => 'row-b',
'product_id' => $productB->id,
'qty' => 1,
'price' => 10.70,
'price_net' => 10.00,
'tax_rate' => 7.00,
'tax' => 0.70,
'price_vk_net' => 10.00,
'discount' => 30.00,
'points' => 0,
'slug' => $productB->slug,
]);
$sourcePayment = ShoppingPayment::create([
'shopping_order_id' => $sourceOrder->id,
'clearingtype' => 'cc',
'reference' => 'source-payment',
'amount' => 28860,
'currency' => 'EUR',
'mode' => 'live',
'is_abo' => true,
'abo_interval' => 5,
]);
$sourcePayment->txaction = 'paid';
$sourcePayment->status = 'APPROVED';
$sourcePayment->save();
PaymentTransaction::create([
'shopping_payment_id' => $sourcePayment->id,
'request' => 'authorization',
'txid' => 111,
'userid' => 222,
'status' => 'APPROVED',
'txaction' => 'paid',
'mode' => 'live',
]);
UserAboOrder::create([
'user_abo_id' => $userAbo->id,
'shopping_order_id' => $sourceOrder->id,
'status' => 2,
'paid' => true,
]);
$originalInvoice = UserInvoice::create([
'shopping_order_id' => $sourceOrder->id,
'year' => now()->year,
'month' => now()->month,
'date' => now(),
'full_number' => '202600001',
'number' => 1,
'filename' => 'original.pdf',
'dir' => 'invoice/2026/06/',
'delivery_filename' => 'delivery.pdf',
'delivery_dir' => 'delivery/2026/06/',
'disk' => 'public',
'cancellation' => true,
'cancellation_id' => 2,
'cancellation_date' => now(),
'status' => 1,
]);
$cancellationInvoice = UserInvoice::create([
'shopping_order_id' => $sourceOrder->id,
'year' => now()->year,
'month' => now()->month,
'date' => now(),
'full_number' => '202600002',
'number' => 2,
'filename' => 'cancellation.pdf',
'dir' => 'invoice/2026/06/',
'delivery_filename' => 'cancellation-delivery.pdf',
'delivery_dir' => 'delivery/2026/06/',
'disk' => 'public',
'cancellation' => true,
'status' => 11,
]);
$originalInvoice->cancellation_id = $cancellationInvoice->id;
$originalInvoice->save();
return [$sourceOrder, $userAbo, $user];
}
it('zeigt die Ersatzbestellung im Trockenlauf ohne Aenderung', function () {
[$sourceOrder, $userAbo, $user] = createAboReplacementOrderSeed();
$exit = Artisan::call('abo:create-replacement-order', [
'--source-order' => (string) $sourceOrder->id,
'--abo' => (string) $userAbo->id,
'--user' => (string) $user->id,
]);
expect($exit)->toBe(0);
expect(ShoppingOrder::where('customer_order_source_comment', 'like', '%replacement_for_order:%')->exists())->toBeFalse();
});
it('erstellt eine steuerfreie Ersatzbestellung ohne neue Zahlung auszuloesen', function () {
[$sourceOrder, $userAbo, $user] = createAboReplacementOrderSeed();
$exit = Artisan::call('abo:create-replacement-order', [
'--fix' => true,
'--force' => true,
'--no-invoice' => true,
'--source-order' => (string) $sourceOrder->id,
'--abo' => (string) $userAbo->id,
'--user' => (string) $user->id,
]);
expect($exit)->toBe(0);
$replacement = ShoppingOrder::where('customer_order_source_comment', 'like', '%replacement_for_order:'.$sourceOrder->id.'%')->first();
expect($replacement)->not->toBeNull()
->and((float) $replacement->subtotal)->toBe(210.00)
->and((float) $replacement->shipping)->toBe(37.00)
->and((float) $replacement->shipping_net)->toBe(31.09)
->and((float) $replacement->tax)->toBe(0.00)
->and((float) $replacement->total_shipping)->toBe(241.09)
->and($replacement->tax_split)->toBeNull()
->and($replacement->net_split)->toBeNull()
->and($replacement->txaction)->toBe('extern_paid')
->and($replacement->shopping_order_items()->count())->toBe(2)
->and(UserAboOrder::where('shopping_order_id', $replacement->id)->where('user_abo_id', $userAbo->id)->exists())->toBeTrue()
->and(ShoppingPayment::where('shopping_order_id', $replacement->id)->value('amount'))->toBe(24109);
$replacement->shopping_order_items->each(function (ShoppingOrderItem $item) {
expect((float) $item->tax_rate)->toBe(0.00)
->and((float) $item->tax)->toBe(0.00)
->and((float) $item->price)->toBe((float) $item->price_net);
});
});