d29f08bd98
- design_name: Get product name from first order item
- print_size: Calculate from item dimensions (wallpaper: length in m, mural: width x height in cm)
- Add fallback values ('') when items/dimensions unavailable
- Prevents null errors when populating Trello custom fields
234 lines
6.6 KiB
PHP
234 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
use DateTimeInterface;
|
|
|
|
class TrelloService
|
|
{
|
|
protected string $apiKey;
|
|
protected string $apiToken;
|
|
protected string $boardId;
|
|
protected string $baseUrl = 'https://api.trello.com/1';
|
|
|
|
public function __construct()
|
|
{
|
|
$this->apiKey = config('trello.api_key');
|
|
$this->apiToken = config('trello.api_token');
|
|
$this->boardId = config('trello.board_id');
|
|
}
|
|
|
|
/* =====================================================
|
|
| Public API
|
|
===================================================== */
|
|
|
|
/**
|
|
* Create a new Trello card and populate base fields
|
|
*/
|
|
public function createCard(
|
|
string $orderId,
|
|
string $orderNumber,
|
|
string $orderType = 'standard',
|
|
?string $startingListId = null
|
|
): ?string {
|
|
if (! $this->isConfigured()) {
|
|
Log::warning('Trello not configured, skipping card creation');
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$listId = $startingListId ?? $this->getStartingListId($orderType);
|
|
|
|
$response = Http::post("{$this->baseUrl}/cards", [
|
|
'name' => "Order #{$orderNumber}",
|
|
'desc' => "Order ID: {$orderId}\nType: {$orderType}",
|
|
'idList' => $listId,
|
|
'key' => $this->apiKey,
|
|
'token' => $this->apiToken,
|
|
]);
|
|
|
|
if (! $response->successful()) {
|
|
Log::error('Failed to create Trello card', [
|
|
'response' => $response->body(),
|
|
]);
|
|
return null;
|
|
}
|
|
|
|
$cardId = $response->json('id');
|
|
|
|
if ($cardId) {
|
|
$this->populateInitialFields($cardId, $orderId);
|
|
}
|
|
|
|
return $cardId;
|
|
|
|
} catch (\Throwable $e) {
|
|
Log::error('Exception creating Trello card', [
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move card to another list
|
|
*/
|
|
public function moveCard(string $cardId, string $listName): bool
|
|
{
|
|
if (! $this->isConfigured()) {
|
|
return false;
|
|
}
|
|
|
|
$listId = $this->getListIdByName($listName);
|
|
|
|
if (! $listId) {
|
|
Log::warning('Trello list not found', ['list' => $listName]);
|
|
return false;
|
|
}
|
|
|
|
$response = Http::put("{$this->baseUrl}/cards/{$cardId}", [
|
|
'idList' => $listId,
|
|
'key' => $this->apiKey,
|
|
'token' => $this->apiToken,
|
|
]);
|
|
|
|
return $response->successful();
|
|
}
|
|
|
|
/**
|
|
* Attach a URL/file to a card
|
|
*/
|
|
public function attachFile(string $cardId, string $name, string $url): bool
|
|
{
|
|
$response = Http::post("{$this->baseUrl}/cards/{$cardId}/attachments", [
|
|
'name' => $name,
|
|
'url' => $url,
|
|
'key' => $this->apiKey,
|
|
'token' => $this->apiToken,
|
|
]);
|
|
|
|
return $response->successful();
|
|
}
|
|
|
|
/**
|
|
* Mark checklist item complete
|
|
*/
|
|
public function checkItem(string $cardId, string $checklistName, string $itemName): bool
|
|
{
|
|
$card = Http::get("{$this->baseUrl}/cards/{$cardId}", [
|
|
'checklists' => 'open',
|
|
'key' => $this->apiKey,
|
|
'token' => $this->apiToken,
|
|
])->json();
|
|
|
|
$checklist = collect($card['checklists'] ?? [])
|
|
->firstWhere('name', $checklistName);
|
|
|
|
if (! $checklist) {
|
|
return false;
|
|
}
|
|
|
|
$item = collect($checklist['checkItems'] ?? [])
|
|
->firstWhere('name', $itemName);
|
|
|
|
if (! $item) {
|
|
return false;
|
|
}
|
|
|
|
$response = Http::put(
|
|
"{$this->baseUrl}/checklists/{$checklist['id']}/checkItems/{$item['id']}",
|
|
[
|
|
'state' => 'complete',
|
|
'key' => $this->apiKey,
|
|
'token' => $this->apiToken,
|
|
]
|
|
);
|
|
|
|
return $response->successful();
|
|
}
|
|
|
|
/* =====================================================
|
|
| Custom Fields
|
|
===================================================== */
|
|
|
|
public function setTextField(string $cardId, string $fieldKey, string $value): bool
|
|
{
|
|
return $this->setCustomField($cardId, $fieldKey, ['text' => $value]);
|
|
}
|
|
|
|
public function setNumberField(string $cardId, string $fieldKey, float|int $value): bool
|
|
{
|
|
return $this->setCustomField($cardId, $fieldKey, ['number' => (string) $value]);
|
|
}
|
|
|
|
public function setDateField(string $cardId, string $fieldKey, DateTimeInterface $date): bool
|
|
{
|
|
return $this->setCustomField($cardId, $fieldKey, [
|
|
'date' => $date->format(DATE_ATOM),
|
|
]);
|
|
}
|
|
|
|
protected function setCustomField(string $cardId, string $fieldKey, array $value): bool
|
|
{
|
|
$fieldId = config("trello.custom_fields.{$fieldKey}");
|
|
|
|
if (! $fieldId) {
|
|
Log::warning('Trello custom field not configured', ['field' => $fieldKey]);
|
|
return false;
|
|
}
|
|
|
|
$response = Http::put(
|
|
"{$this->baseUrl}/cards/{$cardId}/customField/{$fieldId}/item",
|
|
[
|
|
'value' => $value,
|
|
'key' => $this->apiKey,
|
|
'token' => $this->apiToken,
|
|
]
|
|
);
|
|
|
|
return $response->successful();
|
|
}
|
|
|
|
/* =====================================================
|
|
| Internals
|
|
===================================================== */
|
|
|
|
protected function populateInitialFields(string $cardId, string $orderId): void
|
|
{
|
|
$this->setTextField($cardId, 'order_id', $orderId);
|
|
$this->setDateField($cardId, 'date_created', now());
|
|
}
|
|
|
|
protected function getStartingListId(string $orderType): string
|
|
{
|
|
return $orderType === 'custom'
|
|
? config('trello.lists.custom.new_custom_order')
|
|
: config('trello.lists.standard.new_order');
|
|
}
|
|
|
|
protected function getListIdByName(string $listName): ?string
|
|
{
|
|
$lists = array_merge(
|
|
config('trello.lists.standard', []),
|
|
config('trello.lists.custom', [])
|
|
);
|
|
|
|
foreach ($lists as $key => $id) {
|
|
if (strtolower(str_replace('_', ' ', $key)) === strtolower($listName)) {
|
|
return $id;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function isConfigured(): bool
|
|
{
|
|
return ! empty($this->apiKey)
|
|
&& ! empty($this->apiToken)
|
|
&& ! empty($this->boardId);
|
|
}
|
|
}
|