Compare commits

...

2 Commits

6 changed files with 160 additions and 56 deletions

View File

@@ -73,8 +73,6 @@ if (! function_exists('sendSMS')) {
return []; return [];
} }
return [];
$response = Http::withToken('WsTEJTBGlQiJ') $response = Http::withToken('WsTEJTBGlQiJ')
->retry( ->retry(
times: 3, times: 3,

View File

@@ -7,6 +7,7 @@ use App\Http\Requests\CheckoutOrderRequest;
use App\Http\Resources\Api\V1\Order\OrderIndexResource; use App\Http\Resources\Api\V1\Order\OrderIndexResource;
use App\Models\Ecommerce\Product\Order\Order; use App\Models\Ecommerce\Product\Order\Order;
use App\Repositories\Ecommerce\Order\OrderRepository; use App\Repositories\Ecommerce\Order\OrderRepository;
use App\Services\Order\CreateOrderService;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -35,9 +36,9 @@ class OrderController extends Controller
/** /**
* (*) Store order * (*) Store order
*/ */
public function store(CheckoutOrderRequest $request): JsonResponse public function store(CheckoutOrderRequest $request, CreateOrderService $service): JsonResponse
{ {
$order = (new OrderRepository($request->all()))->create(); $order = $service->execute(auth()->user(), $request->validated());
$url = null; $url = null;
if ($request->payment_type_id == 3) { if ($request->payment_type_id == 3) {

View File

@@ -1,58 +1,17 @@
<?php <?php
declare(strict_types=1);
namespace App\Repositories\Ecommerce\Order; namespace App\Repositories\Ecommerce\Order;
use App\Events\Ecommerce\Product\Order\OrderCreated;
use App\Models\Ecommerce\Product\Order\Order;
use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping; use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping;
use Illuminate\Support\Facades\DB;
class OrderRepository class OrderRepository
{ {
/**
* Order repo
*/
public function __construct(
protected array $data = [],
) {}
/**
* Create new order
*/
public function create()
{
$order = Order::create($this->data);
auth()->user()->carts()->with(['product' => [
'media',
'channels',
]])->get()->each(function ($cart) use ($order) {
DB::table('order_items')->insert([
'product_name' => $cart->product->name,
'product_id' => $cart->product_id,
'order_id' => $order->id,
'channel_id' => $cart->product->channels->first()?->id ?? tmpostChannel()->id,
'quantity' => $cart->product_quantity,
'unit_price_amount' => $cart->product->price_amount,
'unit_cost_amount' => $cart->product->cost_amount,
'created_at' => now(),
'updated_at' => now(),
]);
$cart->product->update([
'stock' => $cart->product->stock - $cart->product_quantity,
]);
});
auth()->user()->carts()->delete();
OrderCreated::dispatch($order);
return $order;
}
/** /**
* Available times * Available times
*
* @return array<string, mixed>
*/ */
public static function availableTimes(): array public static function availableTimes(): array
{ {

View File

@@ -1,11 +1,15 @@
<?php <?php
declare(strict_types=1);
namespace App\Repositories\Ecommerce\Product; namespace App\Repositories\Ecommerce\Product;
use App\Helpers\Ecommerce\Product\Filter\ProductFilterer; use App\Helpers\Ecommerce\Product\Filter\ProductFilterer;
use App\Helpers\Ecommerce\Product\Sort\ProductSorter; use App\Helpers\Ecommerce\Product\Sort\ProductSorter;
use App\Models\Ecommerce\Product\Product\Product; use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Contracts\Pagination\Paginator; use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -21,7 +25,7 @@ class ProductRepository
* *
* @var \Illuminate\Database\Eloquent\Builder * @var \Illuminate\Database\Eloquent\Builder
*/ */
protected mixed $queryBuilder; protected Builder $queryBuilder;
/** /**
* Application request * Application request
@@ -31,7 +35,7 @@ class ProductRepository
/** /**
* Relationships to eager load * Relationships to eager load
* *
* @var array<int, string> * @var array<int, string|array>
*/ */
protected array $with = []; protected array $with = [];
@@ -54,6 +58,8 @@ class ProductRepository
/** /**
* Update query builder with resource relationship * Update query builder with resource relationship
*
* @param mixed $resource
*/ */
public function queryAsFromResource($resource): self public function queryAsFromResource($resource): self
{ {
@@ -119,7 +125,7 @@ class ProductRepository
public function applySearchQueries(): self public function applySearchQueries(): self
{ {
if (request()->filled('q')) { if (request()->filled('q')) {
$searcQuery = str_replace([ $searchQuery = str_replace([
'\\', '\\',
'(', '(',
')', ')',
@@ -128,7 +134,7 @@ class ProductRepository
], '', request('q')); ], '', request('q'));
// Search by name // Search by name
$this->queryBuilder->where('products.name', '~*', $searcQuery); $this->queryBuilder->where('products.name', '~*', $searchQuery);
} }
return $this; return $this;
@@ -145,7 +151,7 @@ class ProductRepository
} }
/** /**
* "Where IN" clouse * "Where IN" clause
*/ */
public function whereIn(string $attribute, array $value): self public function whereIn(string $attribute, array $value): self
{ {
@@ -155,7 +161,7 @@ class ProductRepository
} }
/** /**
* "where integer in raw" clouse * "where integer in raw" clause
*/ */
public function whereIntegerInRaw(string $attribute, array $value): self public function whereIntegerInRaw(string $attribute, array $value): self
{ {
@@ -180,7 +186,7 @@ class ProductRepository
/** /**
* Get the results * Get the results
*/ */
public function get() public function get(): Collection
{ {
$this->eagerLoadRelationships(); $this->eagerLoadRelationships();
@@ -257,6 +263,8 @@ class ProductRepository
/** /**
* Ajax paginate * Ajax paginate
*
* @param mixed $products
*/ */
public static function ajaxPaginate($products): JsonResponse public static function ajaxPaginate($products): JsonResponse
{ {

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace App\Services\Order;
use App\Events\Ecommerce\Product\Order\OrderCreated;
use App\Models\Ecommerce\Product\Order\Order;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class CreateOrderService
{
/**
* Create a new order for the user
*
* @param User $user
* @param array $data
* @return Order
*/
public function execute(User $user, array $data): Order
{
return DB::transaction(function () use ($user, $data) {
// 1. Create the order
$order = Order::create($data);
// 2. Process Cart Items
$user->carts()
->with(['product' => function ($query) {
$query->with(['media', 'channels']);
}])
->get()
->each(function ($cart) use ($order) {
// Create Order Item
DB::table('order_items')->insert([
'product_name' => $cart->product->name,
'product_id' => $cart->product_id,
'order_id' => $order->id,
'channel_id' => $cart->product->channels->first()?->id ?? tmpostChannel()->id,
'quantity' => $cart->product_quantity,
'unit_price_amount' => $cart->product->price_amount,
'unit_cost_amount' => $cart->product->cost_amount,
'created_at' => now(),
'updated_at' => now(),
]);
// Update Stock
$cart->product->update([
'stock' => $cart->product->stock - $cart->product_quantity,
]);
});
// 3. Clear User Cart
$user->carts()->delete();
// 4. Dispatch Event
OrderCreated::dispatch($order);
return $order;
});
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Tests\Feature\Services\Order;
use App\Models\Ecommerce\Product\Cart\Cart;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\User;
use App\Services\Order\CreateOrderService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
use App\Events\Ecommerce\Product\Order\OrderCreated;
class CreateOrderServiceTest extends TestCase
{
use RefreshDatabase;
public function test_it_creates_order_successfully()
{
Event::fake();
// 1. Arrange
$user = User::factory()->create();
$product = Product::factory()->create([
'stock' => 10,
'price_amount' => 100,
'cost_amount' => 50,
'name' => 'Test Product'
]);
// Create Cart Item
Cart::factory()->create([
'user_id' => $user->id,
'product_id' => $product->id,
'product_quantity' => 2
]);
$service = new CreateOrderService();
$orderData = [
'user_id' => $user->id,
'status' => 'pending',
'total_amount' => 200
];
// 2. Act
$order = $service->execute($user, $orderData);
// 3. Assert
// Check Order Created
$this->assertDatabaseHas('orders', [
'id' => $order->id,
'user_id' => $user->id,
'total_amount' => 200
]);
// Check Order Items Created
$this->assertDatabaseHas('order_items', [
'order_id' => $order->id,
'product_id' => $product->id,
'quantity' => 2,
'product_name' => 'Test Product'
]);
// Check Stock Deducted (10 - 2 = 8)
$this->assertEquals(8, $product->fresh()->stock);
// Check Cart Cleared
$this->assertDatabaseMissing('carts', [
'user_id' => $user->id
]);
// Check Event Dispatched
Event::assertDispatched(OrderCreated::class);
}
}