feat(v1.5): complete phases 40-43 workflow cleanup

This commit is contained in:
2026-03-25 22:46:51 +01:00
parent b8dda81e7b
commit 3610571949
37 changed files with 1557 additions and 259 deletions

View File

@@ -14,10 +14,19 @@ use Throwable;
final class AutomationController
{
private const ALLOWED_EVENTS = ['receipt.created'];
private const ALLOWED_CONDITION_TYPES = ['integration'];
private const ALLOWED_EVENTS = ['receipt.created', 'shipment.status_changed'];
private const ALLOWED_CONDITION_TYPES = ['integration', 'shipment_status'];
private const ALLOWED_ACTION_TYPES = ['send_email'];
private const ALLOWED_RECIPIENTS = ['client', 'client_and_company', 'company'];
private const SHIPMENT_STATUS_OPTIONS = [
'registered' => ['label' => 'Przesylka zarejestrowana', 'statuses' => ['created', 'confirmed']],
'ready_for_pickup' => ['label' => 'Przesylka do odbioru', 'statuses' => ['ready_for_pickup']],
'dropped_at_point' => ['label' => 'Przesylka nadana w punkcie', 'statuses' => ['confirmed', 'in_transit']],
'picked_up' => ['label' => 'Przesylka odebrana', 'statuses' => ['delivered']],
'cancelled' => ['label' => 'Przesylka anulowana', 'statuses' => ['cancelled']],
'unclaimed' => ['label' => 'Przesylka nieodebrana', 'statuses' => ['problem']],
'picked_up_return' => ['label' => 'Przesylka odebrana (zwrot)', 'statuses' => ['returned']],
];
public function __construct(
private readonly Template $template,
@@ -185,6 +194,7 @@ final class AutomationController
'conditionTypes' => self::ALLOWED_CONDITION_TYPES,
'actionTypes' => self::ALLOWED_ACTION_TYPES,
'recipientOptions' => self::ALLOWED_RECIPIENTS,
'shipmentStatusOptions' => self::SHIPMENT_STATUS_OPTIONS,
'errorMessage' => Flash::get('settings.automation.error', ''),
], 'layouts/app');
@@ -289,6 +299,21 @@ final class AutomationController
return count($integrationIds) > 0 ? ['integration_ids' => $integrationIds] : null;
}
if ($type === 'shipment_status') {
$keys = $condition['shipment_status_keys'] ?? [];
if (!is_array($keys)) {
$keys = [];
}
$allowedKeys = array_keys(self::SHIPMENT_STATUS_OPTIONS);
$statusKeys = array_values(array_filter(
array_map(static fn (mixed $key): string => trim((string) $key), $keys),
static fn (string $key): bool => $key !== '' && in_array($key, $allowedKeys, true)
));
return count($statusKeys) > 0 ? ['status_keys' => array_values(array_unique($statusKeys))] : null;
}
return null;
}

View File

@@ -10,6 +10,16 @@ use Throwable;
final class AutomationService
{
private const SHIPMENT_STATUS_OPTION_MAP = [
'registered' => ['created', 'confirmed'],
'ready_for_pickup' => ['ready_for_pickup'],
'dropped_at_point' => ['confirmed', 'in_transit'],
'picked_up' => ['delivered'],
'cancelled' => ['cancelled'],
'unclaimed' => ['problem'],
'picked_up_return' => ['returned'],
];
public function __construct(
private readonly AutomationRepository $repository,
private readonly EmailSendingService $emailService,
@@ -18,7 +28,10 @@ final class AutomationService
) {
}
public function trigger(string $eventType, int $orderId): void
/**
* @param array<string, mixed> $context
*/
public function trigger(string $eventType, int $orderId, array $context = []): void
{
$rules = $this->repository->findActiveByEvent($eventType);
if ($rules === []) {
@@ -38,7 +51,7 @@ final class AutomationService
$actions = is_array($rule['actions'] ?? null) ? $rule['actions'] : [];
$ruleName = (string) ($rule['name'] ?? '');
if ($this->evaluateConditions($conditions, $order)) {
if ($this->evaluateConditions($conditions, $order, $context)) {
$this->executeActions($actions, $orderId, $ruleName);
}
} catch (Throwable) {
@@ -50,14 +63,15 @@ final class AutomationService
/**
* @param list<array<string, mixed>> $conditions
* @param array<string, mixed> $order
* @param array<string, mixed> $context
*/
private function evaluateConditions(array $conditions, array $order): bool
private function evaluateConditions(array $conditions, array $order, array $context): bool
{
foreach ($conditions as $condition) {
$type = (string) ($condition['condition_type'] ?? '');
$value = is_array($condition['condition_value'] ?? null) ? $condition['condition_value'] : [];
if (!$this->evaluateSingleCondition($type, $value, $order)) {
if (!$this->evaluateSingleCondition($type, $value, $order, $context)) {
return false;
}
}
@@ -68,12 +82,16 @@ final class AutomationService
/**
* @param array<string, mixed> $value
* @param array<string, mixed> $order
* @param array<string, mixed> $context
*/
private function evaluateSingleCondition(string $type, array $value, array $order): bool
private function evaluateSingleCondition(string $type, array $value, array $order, array $context): bool
{
if ($type === 'integration') {
return $this->evaluateIntegrationCondition($value, $order);
}
if ($type === 'shipment_status') {
return $this->evaluateShipmentStatusCondition($value, $context);
}
return false;
}
@@ -97,6 +115,40 @@ final class AutomationService
return in_array($orderIntegrationId, array_map('intval', $allowedIds), true);
}
/**
* @param array<string, mixed> $value
* @param array<string, mixed> $context
*/
private function evaluateShipmentStatusCondition(array $value, array $context): bool
{
$statusKeys = is_array($value['status_keys'] ?? null) ? $value['status_keys'] : [];
if ($statusKeys === []) {
return false;
}
$deliveryStatus = trim((string) ($context['delivery_status'] ?? ''));
if ($deliveryStatus === '') {
return false;
}
$allowedStatuses = [];
foreach ($statusKeys as $statusKeyRaw) {
$statusKey = trim((string) $statusKeyRaw);
if ($statusKey === '' || !isset(self::SHIPMENT_STATUS_OPTION_MAP[$statusKey])) {
continue;
}
foreach (self::SHIPMENT_STATUS_OPTION_MAP[$statusKey] as $mappedStatus) {
$allowedStatuses[$mappedStatus] = true;
}
}
if ($allowedStatuses === []) {
return false;
}
return isset($allowedStatuses[$deliveryStatus]);
}
/**
* @param list<array<string, mixed>> $actions
*/