244 lines
7.6 KiB
PHP
244 lines
7.6 KiB
PHP
<?php
|
|
|
|
use App\Models\Ecommerce\Channel\Channel;
|
|
use App\Models\Ecommerce\Product\Cart\CartItem;
|
|
use App\Models\Ecommerce\Product\Order\Order;
|
|
use App\Models\Ecommerce\Product\Order\Payment\OrderPayment;
|
|
use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping;
|
|
use App\Models\Ecommerce\Product\Order\Status\OrderStatus;
|
|
use App\Models\Ecommerce\Product\Product\Product;
|
|
use App\Models\System\Settings\Location\Region;
|
|
use App\Models\System\Settings\Payments\PaymentType;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Config;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
Config::set('ecommerce.api.token', 'test-token');
|
|
|
|
$this->user = User::factory()->create([
|
|
'password' => 'password',
|
|
'phone_number' => 61929248,
|
|
]);
|
|
|
|
// Create tmpost channel required by OrderRepository
|
|
if (Channel::where('slug', 'tmpost')->doesntExist()) {
|
|
Channel::create(['slug' => 'tmpost', 'name' => 'TM Post']);
|
|
}
|
|
|
|
// Mock default payment type for OrderPayment::default()
|
|
if (PaymentType::count() === 0) {
|
|
$paymentType = new PaymentType();
|
|
$paymentType->forceFill([
|
|
'id' => 1,
|
|
'code' => 'cash',
|
|
'name' => ['en' => 'Cash'],
|
|
'tax' => 0,
|
|
'is_enabled' => true,
|
|
]);
|
|
$paymentType->save();
|
|
}
|
|
});
|
|
|
|
test('authenticated user can list their orders', function () {
|
|
$order = new Order();
|
|
$order->forceFill([
|
|
'user_id' => $this->user->id,
|
|
'number' => 'ORD-123',
|
|
'customer_name' => 'John Doe',
|
|
'customer_phone' => 61929248,
|
|
'customer_address' => 'Test Address',
|
|
'shipping_method' => OrderShipping::STANDART,
|
|
'shipping_price' => 20,
|
|
'payment_type_id' => 1, // Assuming ID 1 exists or mocked
|
|
'region' => Region::AG,
|
|
'delivery_at' => now(),
|
|
'status' => OrderStatus::PENDING,
|
|
]);
|
|
$order->save();
|
|
|
|
$response = $this->actingAs($this->user, 'sanctum')
|
|
->withHeaders(['Api-Token' => 'test-token'])
|
|
->getJson('/api/v1/orders');
|
|
|
|
$response->assertOk()
|
|
->assertJsonStructure([
|
|
'data' => [
|
|
'*' => [
|
|
'id',
|
|
'number',
|
|
// Add other fields from OrderIndexResource
|
|
]
|
|
]
|
|
]);
|
|
});
|
|
|
|
test('unauthenticated user cannot list orders', function () {
|
|
$this->withHeaders(['Api-Token' => 'test-token'])
|
|
->getJson('/api/v1/orders')
|
|
->assertUnauthorized();
|
|
});
|
|
|
|
test('can place an order', function () {
|
|
// Setup prerequisites
|
|
$product = Product::create([
|
|
'name' => 'Test Product',
|
|
'slug' => 'test-product',
|
|
'price_amount' => 100,
|
|
'cost_amount' => 80,
|
|
'stock' => 10,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
// Add item to cart
|
|
CartItem::create([
|
|
'user_id' => $this->user->id,
|
|
'product_id' => $product->id,
|
|
'product_quantity' => 2,
|
|
]);
|
|
|
|
// Mock PaymentType if needed (OrderPayment::values() uses DB)
|
|
PaymentType::create([
|
|
'code' => 'card',
|
|
'name' => ['en' => 'Card'],
|
|
'tax' => 0,
|
|
'is_enabled' => true,
|
|
]);
|
|
$paymentTypeId = PaymentType::where('code', 'card')->first()->id;
|
|
|
|
$response = $this->actingAs($this->user, 'sanctum')
|
|
->withHeaders(['Api-Token' => 'test-token'])
|
|
->postJson('/api/v1/orders', [
|
|
'customer_name' => 'John Doe',
|
|
'customer_phone' => 61929248,
|
|
'customer_address' => '123 Main St',
|
|
'shipping_method' => OrderShipping::STANDART,
|
|
'payment_type_id' => $paymentTypeId,
|
|
'region' => Region::AG,
|
|
'notes' => 'Test order',
|
|
]);
|
|
|
|
$response->assertCreated()
|
|
->assertJsonStructure(['data' => ['order_id']]);
|
|
|
|
// Verify order created
|
|
$this->assertDatabaseHas('orders', [
|
|
'user_id' => $this->user->id,
|
|
'customer_name' => 'John Doe',
|
|
'notes' => 'Test order',
|
|
]);
|
|
|
|
// Verify cart cleared
|
|
$this->assertDatabaseMissing('cart_items', [
|
|
'user_id' => $this->user->id,
|
|
]);
|
|
|
|
// Verify stock reduced
|
|
$this->assertEquals(8, $product->fresh()->stock);
|
|
});
|
|
|
|
test('cannot place order with empty cart', function () {
|
|
// Ensure cart is empty
|
|
$this->user->carts()->delete();
|
|
|
|
// Mock PaymentType
|
|
PaymentType::create([
|
|
'code' => 'online', // unique code
|
|
'name' => ['en' => 'Online'],
|
|
'tax' => 0,
|
|
'is_enabled' => true,
|
|
]);
|
|
$paymentTypeId = PaymentType::where('code', 'online')->first()->id;
|
|
|
|
$response = $this->actingAs($this->user, 'sanctum')
|
|
->withHeaders(['Api-Token' => 'test-token'])
|
|
->postJson('/api/v1/orders', [
|
|
'customer_name' => 'John Doe',
|
|
'customer_phone' => 61929248,
|
|
'customer_address' => '123 Main St',
|
|
'shipping_method' => OrderShipping::STANDART,
|
|
'payment_type_id' => $paymentTypeId,
|
|
'region' => Region::AG,
|
|
]);
|
|
|
|
// The validation 'user_without_product_in_cart' should fail
|
|
$response->assertStatus(422)
|
|
->assertJsonValidationErrors(['user_without_product_in_cart']);
|
|
});
|
|
|
|
test('order validation fails with invalid data', function () {
|
|
// Add item to cart to pass that check
|
|
$product = Product::create(['name' => 'P', 'slug' => 'p', 'stock' => 1]);
|
|
CartItem::create(['user_id' => $this->user->id, 'product_id' => $product->id, 'product_quantity' => 1]);
|
|
|
|
$response = $this->actingAs($this->user, 'sanctum')
|
|
->withHeaders(['Api-Token' => 'test-token'])
|
|
->postJson('/api/v1/orders', [
|
|
'customer_name' => '', // Required
|
|
'shipping_method' => 'invalid_method', // Invalid enum
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJsonValidationErrors(['customer_name', 'shipping_method']);
|
|
});
|
|
|
|
test('can show specific order', function () {
|
|
$order = new Order();
|
|
$order->forceFill([
|
|
'user_id' => $this->user->id,
|
|
'number' => 'ORD-SHOW',
|
|
'customer_name' => 'John Doe',
|
|
'customer_phone' => 61929248,
|
|
'customer_address' => 'Address',
|
|
'shipping_method' => OrderShipping::STANDART,
|
|
'shipping_price' => 20,
|
|
'payment_type_id' => 1,
|
|
'region' => Region::AG,
|
|
'delivery_at' => now(),
|
|
'status' => OrderStatus::PENDING,
|
|
]);
|
|
$order->save();
|
|
|
|
$response = $this->actingAs($this->user, 'sanctum')
|
|
->withHeaders(['Api-Token' => 'test-token'])
|
|
->getJson("/api/v1/orders/{$order->id}");
|
|
|
|
$response->assertOk()
|
|
->assertJsonPath('data.id', $order->id)
|
|
->assertJsonPath('data.number', 'ORD-SHOW');
|
|
});
|
|
|
|
test('can delete order (if allowed)', function () {
|
|
// Note: The controller destroy method is basically empty: return response()->rest();
|
|
// But the route exists. Let's test it returns 200 at least.
|
|
|
|
$order = new Order();
|
|
$order->forceFill([
|
|
'user_id' => $this->user->id,
|
|
'number' => 'ORD-DEL',
|
|
'customer_name' => 'John Doe',
|
|
'customer_phone' => 61929248,
|
|
'customer_address' => 'Address',
|
|
'shipping_method' => OrderShipping::STANDART,
|
|
'shipping_price' => 20,
|
|
'payment_type_id' => 1,
|
|
'region' => Region::AG,
|
|
'delivery_at' => now(),
|
|
'status' => OrderStatus::PENDING,
|
|
]);
|
|
$order->save();
|
|
|
|
$response = $this->actingAs($this->user, 'sanctum')
|
|
->withHeaders(['Api-Token' => 'test-token'])
|
|
->deleteJson("/api/v1/orders/{$order->id}");
|
|
|
|
$response->assertOk();
|
|
|
|
$this->assertSoftDeleted('orders', [
|
|
'id' => $order->id,
|
|
]);
|
|
});
|