5cd8c05a7c
- Create InspectionPassed event - Create MoveCardToPackingOnInspectionPassed listener - Register event listener in EventServiceProvider - Dispatch event when markInspectionPassed() is called Now when an order passes inspection on the ops page, the Trello card automatically moves from Inspection to Packing list.
196 lines
6.4 KiB
PHP
196 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Events\ReadyToShipIntent;
|
|
use App\Models\Order;
|
|
use App\Models\CustomOrder;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class TrelloWebhookController extends Controller
|
|
{
|
|
/**
|
|
* Handle incoming Trello webhooks
|
|
*
|
|
* POST /api/webhooks/trello
|
|
*/
|
|
public function handle(Request $request)
|
|
{
|
|
// Trello validation ping or real event - both return 200
|
|
Log::info('Trello webhook received', [
|
|
'method' => $request->method(),
|
|
'content_length' => strlen($request->getContent()),
|
|
]);
|
|
|
|
// If empty body or validation ping, just return success
|
|
if ($request->getContent() === '' || $request->method() === 'HEAD') {
|
|
return response()->json(['ok' => true], 200);
|
|
}
|
|
|
|
try {
|
|
$payload = $request->json()->all();
|
|
|
|
Log::info('Trello action received', [
|
|
'action' => $payload['action']['type'] ?? 'unknown',
|
|
'card' => $payload['action']['data']['card']['name'] ?? 'unknown',
|
|
]);
|
|
|
|
match ($payload['action']['type'] ?? null) {
|
|
'updateCard' => $this->handleCardUpdate($payload),
|
|
'updateCheckItem' => $this->handleChecklistUpdate($payload),
|
|
default => Log::debug('Unhandled Trello action', [
|
|
'type' => $payload['action']['type'] ?? 'unknown',
|
|
]),
|
|
};
|
|
|
|
return response()->json(['success' => true], 200);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Error processing Trello webhook', [
|
|
'error' => $e->getMessage(),
|
|
'trace' => $e->getTraceAsString(),
|
|
]);
|
|
|
|
return response()->json(['success' => true], 200);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Handle card movement between lists
|
|
*/
|
|
private function handleCardUpdate(array $payload): void
|
|
{
|
|
$action = $payload['action'] ?? [];
|
|
$cardData = $action['data']['card'] ?? [];
|
|
$cardId = $cardData['id'] ?? null;
|
|
$cardName = $cardData['name'] ?? null;
|
|
$listAfter = $action['data']['listAfter'] ?? [];
|
|
$listName = $listAfter['name'] ?? null;
|
|
|
|
if (! $cardId || ! $cardName || ! $listName) {
|
|
Log::warning('Trello card update missing required data', [
|
|
'card_id' => $cardId,
|
|
'card_name' => $cardName,
|
|
'list_name' => $listName,
|
|
]);
|
|
return;
|
|
}
|
|
|
|
// Extract order number from card name (e.g., "Order #1043" or "Order #ORD-20260102-ABC123")
|
|
if (! preg_match('/Order #([A-Za-z0-9\-]+)/', $cardName, $matches)) {
|
|
Log::debug('Could not extract order number from card name', ['card_name' => $cardName]);
|
|
return;
|
|
}
|
|
|
|
$orderNumber = $matches[1];
|
|
|
|
// Try to find the order (could be standard or custom)
|
|
$order = Order::where('order_number', $orderNumber)->first()
|
|
?? CustomOrder::where('order_number', $orderNumber)->first();
|
|
|
|
if (! $order) {
|
|
Log::warning('Order not found for Trello card', [
|
|
'order_number' => $orderNumber,
|
|
'card_name' => $cardName,
|
|
]);
|
|
return;
|
|
}
|
|
|
|
Log::info('Trello card moved', [
|
|
'order_uuid' => $order->uuid,
|
|
'order_number' => $orderNumber,
|
|
'list' => $listName,
|
|
]);
|
|
|
|
// Handle list-specific actions
|
|
match ($listName) {
|
|
'Prep' => $order->update(['status' => 'prep']),
|
|
'Printing' => $order->update(['status' => 'printing']),
|
|
'Inspection' => $order->update(['status' => 'inspection']),
|
|
'Packing' => $order->update(['status' => 'packing']),
|
|
'Ready to Ship' => $this->handleReadyToShipIntent($order, $cardId),
|
|
'Awaiting Collection' => $this->handleAwaitingCollectionIntent($order, $cardId),
|
|
'In Transit' => $order->update(['status' => 'in_transit']),
|
|
'Done' => $order->update(['status' => 'completed']),
|
|
default => Log::debug('Card moved to list', [
|
|
'order_uuid' => $order->uuid,
|
|
'list' => $listName,
|
|
]),
|
|
};
|
|
|
|
Log::info('Order status updated from Trello', [
|
|
'order_uuid' => $order->uuid,
|
|
'list' => $listName,
|
|
'status' => $order->status,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Handle checklist item completion
|
|
*/
|
|
private function handleChecklistUpdate(array $payload): void
|
|
{
|
|
$action = $payload['action'] ?? [];
|
|
$cardData = $action['data']['card'] ?? [];
|
|
$cardName = $cardData['name'] ?? null;
|
|
$itemName = $action['data']['checkItem']['name'] ?? null;
|
|
$itemState = $action['data']['checkItem']['state'] ?? null;
|
|
|
|
if ($itemState !== 'complete' || ! $cardName || ! $itemName) {
|
|
return;
|
|
}
|
|
|
|
Log::debug('Trello checklist item completed', [
|
|
'card' => $cardName,
|
|
'item' => $itemName,
|
|
]);
|
|
|
|
// TODO: Map checklist completions to domain events if needed
|
|
// For now, just log for visibility
|
|
}
|
|
|
|
/**
|
|
* Handle "Ready to Ship" intent
|
|
*
|
|
* Emit event to trigger shipment creation flow
|
|
*/
|
|
private function handleReadyToShipIntent($order, string $cardId): void
|
|
{
|
|
Log::info('Ready to Ship intent from Trello', [
|
|
'order_uuid' => $order->uuid,
|
|
'card_id' => $cardId,
|
|
]);
|
|
|
|
// Emit event so ShippingController can validate and create shipment
|
|
ReadyToShipIntent::dispatch($order);
|
|
}
|
|
|
|
/**
|
|
* Handle "Awaiting Collection" intent
|
|
*
|
|
* Verify shipment exists before accepting transition
|
|
*/
|
|
private function handleAwaitingCollectionIntent($order, string $cardId): void
|
|
{
|
|
Log::info('Awaiting Collection intent from Trello', [
|
|
'order_uuid' => $order->uuid,
|
|
'card_id' => $cardId,
|
|
]);
|
|
|
|
// Check if order has a waybill (shipment was created)
|
|
if (! $order->courier_waybill_id) {
|
|
Log::warning('Cannot move to Awaiting Collection - no shipment created', [
|
|
'order_uuid' => $order->uuid,
|
|
]);
|
|
return;
|
|
}
|
|
|
|
Log::info('Order ready for collection', [
|
|
'order_uuid' => $order->uuid,
|
|
'waybill_id' => $order->courier_waybill_id,
|
|
]);
|
|
}
|
|
}
|