This commit is contained in:
2026-04-25 21:31:50 +02:00
parent 4931c55338
commit 4b998ea5be
16 changed files with 697 additions and 75 deletions

View File

@@ -534,12 +534,17 @@ final class AutomationController
if ($type === 'send_email') {
$templateId = (int) ($action['template_id'] ?? 0);
$recipient = (string) ($action['recipient'] ?? '');
$sendOncePerOrder = isset($action['send_once_per_order']) && (int) $action['send_once_per_order'] === 1 ? 1 : 0;
if ($templateId <= 0 || !in_array($recipient, self::ALLOWED_RECIPIENTS, true)) {
return null;
}
return ['template_id' => $templateId, 'recipient' => $recipient];
return [
'template_id' => $templateId,
'recipient' => $recipient,
'send_once_per_order' => $sendOncePerOrder,
];
}
if ($type === 'issue_receipt') {

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace App\Modules\Automation;
use PDO;
final class AutomationEmailOnceRepository
{
public function __construct(private readonly PDO $pdo)
{
}
public function wasSent(int $ruleId, int $actionId, int $orderId): bool
{
if ($ruleId <= 0 || $actionId <= 0 || $orderId <= 0) {
return false;
}
$statement = $this->pdo->prepare(
'SELECT 1
FROM automation_email_once_deliveries
WHERE rule_id = :rule_id
AND action_id = :action_id
AND order_id = :order_id
LIMIT 1'
);
$statement->execute([
'rule_id' => $ruleId,
'action_id' => $actionId,
'order_id' => $orderId,
]);
return $statement->fetchColumn() !== false;
}
public function markSent(int $ruleId, int $actionId, int $orderId): void
{
if ($ruleId <= 0 || $actionId <= 0 || $orderId <= 0) {
return;
}
$statement = $this->pdo->prepare(
'INSERT INTO automation_email_once_deliveries (rule_id, action_id, order_id, created_at)
VALUES (:rule_id, :action_id, :order_id, NOW())
ON DUPLICATE KEY UPDATE created_at = created_at'
);
$statement->execute([
'rule_id' => $ruleId,
'action_id' => $actionId,
'order_id' => $orderId,
]);
}
}

View File

@@ -34,6 +34,7 @@ final class AutomationService
public function __construct(
private readonly AutomationRepository $repository,
private readonly AutomationExecutionLogRepository $executionLogs,
private readonly AutomationEmailOnceRepository $emailOnceRepository,
private readonly EmailSendingService $emailService,
private readonly OrdersRepository $orders,
private readonly CompanySettingsRepository $companySettings,
@@ -78,11 +79,12 @@ final class AutomationService
$conditions = is_array($rule['conditions'] ?? null) ? $rule['conditions'] : [];
$actions = is_array($rule['actions'] ?? null) ? $rule['actions'] : [];
$ruleName = (string) ($rule['name'] ?? '');
$ruleId = (int) ($rule['id'] ?? 0);
$ruleContext = $this->withExecution($context, $executionKey);
$ruleMatched = $this->evaluateConditions($conditions, $order, $ruleContext);
if ($ruleMatched) {
$this->executeActions($actions, $orderId, $ruleName, $ruleContext);
$this->executeActions($actions, $orderId, $ruleId, $ruleName, $ruleContext);
$this->logExecution($eventType, $ruleId, $ruleName, $orderId, 'success', 'Wykonano akcje automatyzacji', $ruleContext);
}
} catch (Throwable $exception) {
@@ -282,8 +284,14 @@ final class AutomationService
return false;
}
$newStatus = strtolower(trim((string) ($context['new_status'] ?? '')));
if ($newStatus === '') {
$statusCandidate = (string) ($context['new_status'] ?? '');
if ($statusCandidate === '') {
// order.status_aged carries "current_status" instead of "new_status"
$statusCandidate = (string) ($context['current_status'] ?? '');
}
$normalizedStatus = strtolower(trim($statusCandidate));
if ($normalizedStatus === '') {
return false;
}
@@ -292,7 +300,7 @@ final class AutomationService
$orderStatusCodes
);
return in_array($newStatus, $normalizedCodes, true);
return in_array($normalizedStatus, $normalizedCodes, true);
}
/**
@@ -315,14 +323,15 @@ final class AutomationService
* @param list<array<string, mixed>> $actions
* @param array<string, mixed> $context
*/
private function executeActions(array $actions, int $orderId, string $ruleName, array $context): void
private function executeActions(array $actions, int $orderId, int $ruleId, string $ruleName, array $context): void
{
foreach ($actions as $action) {
$type = (string) ($action['action_type'] ?? '');
$config = is_array($action['action_config'] ?? null) ? $action['action_config'] : [];
$actionId = (int) ($action['id'] ?? 0);
if ($type === 'send_email') {
$this->handleSendEmail($config, $orderId, $ruleName);
$this->handleSendEmail($config, $orderId, $ruleId, $actionId, $ruleName);
continue;
}
@@ -345,26 +354,43 @@ final class AutomationService
/**
* @param array<string, mixed> $config
*/
private function handleSendEmail(array $config, int $orderId, string $ruleName): void
private function handleSendEmail(array $config, int $orderId, int $ruleId, int $actionId, string $ruleName): void
{
$templateId = (int) ($config['template_id'] ?? 0);
if ($templateId <= 0) {
return;
}
$sendOncePerOrder = (int) ($config['send_once_per_order'] ?? 0) === 1;
if (
$sendOncePerOrder
&& $ruleId > 0
&& $actionId > 0
&& $this->emailOnceRepository->wasSent($ruleId, $actionId, $orderId)
) {
return;
}
$recipient = (string) ($config['recipient'] ?? 'client');
$actorName = 'Automatyzacja: ' . $ruleName;
$wasSentSuccessfully = false;
if ($recipient === 'client' || $recipient === 'client_and_company') {
$this->emailService->send($orderId, $templateId, null, $actorName);
$result = $this->emailService->send($orderId, $templateId, null, $actorName);
$wasSentSuccessfully = $wasSentSuccessfully || (bool) ($result['success'] ?? false);
}
if ($recipient === 'company' || $recipient === 'client_and_company') {
$this->sendToCompany($orderId, $templateId, $actorName);
$wasSentSuccessfully = $this->sendToCompany($orderId, $templateId, $actorName) || $wasSentSuccessfully;
}
if ($sendOncePerOrder && $ruleId > 0 && $actionId > 0 && $wasSentSuccessfully) {
$this->emailOnceRepository->markSent($ruleId, $actionId, $orderId);
}
}
private function sendToCompany(int $orderId, int $templateId, string $actorName): void
private function sendToCompany(int $orderId, int $templateId, string $actorName): bool
{
$settings = $this->companySettings->getSettings();
$companyEmail = trim((string) ($settings['email'] ?? ''));
@@ -378,12 +404,12 @@ final class AutomationService
'system',
$actorName
);
return;
return false;
}
$companyName = trim((string) ($settings['company_name'] ?? ''));
$this->emailService->send(
$result = $this->emailService->send(
$orderId,
$templateId,
null,
@@ -391,6 +417,8 @@ final class AutomationService
$companyEmail,
$companyName
);
return (bool) ($result['success'] ?? false);
}
/**

View File

@@ -11,6 +11,7 @@ use App\Modules\Accounting\ReceiptService;
use App\Modules\Automation\AutomationRepository;
use App\Modules\Automation\AutomationService;
use App\Modules\Automation\AutomationExecutionLogRepository;
use App\Modules\Automation\AutomationEmailOnceRepository;
use App\Modules\Automation\OrderStatusAgedService;
use App\Modules\Email\AttachmentGenerator;
use App\Modules\Email\EmailSendingService;
@@ -232,6 +233,7 @@ final class CronHandlerFactory
return new AutomationService(
$automationRepository,
$executionLogRepository,
new AutomationEmailOnceRepository($this->db),
$emailService,
$ordersRepository,
$companySettingsRepository,