ifnex/04_Laravel/app/Http/Controllers/Api/StaffOrderController.php
Kazem Alghasi 61a2643dd0 feat(api): implement shipment review and resubmission workflow
Introduces a complete shipment review system allowing staff to request
changes to customer orders and customers to resubmit corrected orders.

- Add `ReviewState` enum and `ShipmentReview` model to track review history.
- Implement `ShipmentReviewService` to handle approval and change
  request logic.
- Add `resubmit` endpoint for customers to update orders when
  `changes_requested` state is active.
- Add `request-changes` endpoint for staff to flag orders for correction.
- Update `ShipmentResource` in Filament to display review states and
  manage approvals.
- Implement WordPress bridge support for fetching and resubmitting
  orders via AJAX.
- Add database migrations for `shipment_reviews` table and `review_state`
  column on shipments.
- Add `StaffApiMiddleware` to secure staff-specific API routes.
2026-09-27 01:00:26 +03:30

238 lines
7.4 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Shipment;
use App\Models\ShipmentReview;
use App\Services\ShipmentReviewService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class StaffOrderController extends Controller
{
/**
* لیست سفارشات در انتظار تأیید
* GET /api/v1/staff/orders/pending-approval
*/
public function pendingApproval(Request $request): JsonResponse
{
$validated = $request->validate([
'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
]);
$shipments = Shipment::query()
->where('status', ShipmentStatus::PendingApproval)
->with(['fromCountry', 'toCountry', 'user'])
->orderBy('created_at', 'asc')
->paginate($validated['per_page'] ?? 15);
return response()->json([
'success' => true,
'data' => $shipments->map(function ($shipment) {
return $this->formatShipment($shipment);
}),
'pagination' => [
'current_page' => $shipments->currentPage(),
'last_page' => $shipments->lastPage(),
'per_page' => $shipments->perPage(),
'total' => $shipments->total(),
],
]);
}
/**
* درخواست اصلاح سفارش توسط کارمند
* POST /api/v1/staff/orders/{shipment}/request-changes
*/
public function requestChanges(
Shipment $shipment,
Request $request,
ShipmentReviewService $reviewService
): JsonResponse {
$user = Auth::user();
$validated = $request->validate([
'reason' => ['required', 'string', 'max:1000'],
'notes' => ['nullable', 'string', 'max:1000'],
]);
try {
$shipment = $reviewService->requestChanges(
$shipment,
$user,
$validated['reason'],
$validated['notes'] ?? null
);
return response()->json([
'success' => true,
'message' => 'اصلاحات موردنیاز برای سفارش ثبت شد.',
'data' => $this->formatShipment(
$shipment->load(['fromCountry', 'toCountry'])
),
]);
} catch (\RuntimeException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
} catch (\Throwable $e) {
return response()->json([
'success' => false,
'message' => 'خطا در ثبت درخواست اصلاح سفارش.',
], 500);
}
}
/**
* تأیید سفارش توسط کارمند
* POST /api/v1/staff/orders/{shipment}/approve
*/
public function approve(
Shipment $shipment,
Request $request,
ShipmentReviewService $reviewService
): JsonResponse {
$user = Auth::user();
$validated = $request->validate([
'notes' => ['nullable', 'string', 'max:1000'],
]);
try {
$shipment = $reviewService->approve(
$shipment,
$user,
$validated['notes'] ?? null
);
return response()->json([
'success' => true,
'message' => 'سفارش با موفقیت تأیید شد. مشتری می‌تواند پرداخت را انجام دهد.',
'data' => $this->formatShipment(
$shipment->load(['fromCountry', 'toCountry'])
),
]);
} catch (\RuntimeException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
} catch (\Throwable $e) {
return response()->json([
'success' => false,
'message' => 'خطا در تأیید سفارش.',
], 500);
}
}
/**
* رد سفارش توسط کارمند
* POST /api/v1/staff/orders/{shipment}/reject
*/
public function reject(
Shipment $shipment,
Request $request,
ShipmentReviewService $reviewService
): JsonResponse {
$user = Auth::user();
$validated = $request->validate([
'reason' => ['required', 'string', 'max:1000'],
]);
try {
$shipment = $reviewService->reject(
$shipment,
$user,
$validated['reason']
);
return response()->json([
'success' => true,
'message' => 'سفارش رد شد.',
'data' => $this->formatShipment(
$shipment->load(['fromCountry', 'toCountry'])
),
]);
} catch (\RuntimeException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
} catch (\Throwable $e) {
return response()->json([
'success' => false,
'message' => 'خطا در رد سفارش.',
], 500);
}
}
/**
* فرمت کردن Shipment برای خروجی API
*/
private function formatShipment(Shipment $shipment): array
{
return [
'id' => $shipment->id,
'awb_no' => $shipment->awb_no,
'direction' => $shipment->direction?->value,
'type' => $shipment->type?->value,
'status' => [
'value' => $shipment->status?->value,
'label' => $shipment->status?->label(),
'color' => $shipment->status?->color(),
],
'review' => [
'state' => $shipment->review_state?->value,
'label' => $shipment->review_state?->label(),
'color' => $shipment->review_state?->color(),
],
'from_country' => $shipment->fromCountry ? [
'id' => $shipment->fromCountry->id,
'name' => $shipment->fromCountry->name,
'iso_code' => $shipment->fromCountry->iso_code,
] : null,
'to_country' => $shipment->toCountry ? [
'id' => $shipment->toCountry->id,
'name' => $shipment->toCountry->name,
'iso_code' => $shipment->toCountry->iso_code,
] : null,
'user' => $shipment->user ? [
'id' => $shipment->user->id,
'name' => $shipment->user->name,
'email' => $shipment->user->email,
] : null,
'weight' => $shipment->weight,
'chargeable_weight' => $shipment->chargeable_weight,
'total_fee' => $shipment->total_fee,
'created_at' => $shipment->created_at->toIso8601String(),
'created_at_jalali' => $this->toJalali($shipment->created_at),
];
}
/**
* تبدیل تاریخ به شمسی
*/
private function toJalali($date): string
{
if (!$date) {
return '—';
}
try {
return \Morilog\Jalali\Jalalian::fromCarbon($date)->format('Y/m/d H:i');
} catch (\Exception $e) {
return $date->format('Y-m-d H:i');
}
}
}