This commit is contained in:
2026-04-12 01:35:19 +02:00
parent 91a8b85f38
commit d04e02020c
70 changed files with 8634 additions and 207 deletions

View File

@@ -17,7 +17,7 @@ final class AutomationController
{
private const HISTORY_PER_PAGE = 25;
private const ALLOWED_EVENTS = ['receipt.created', 'shipment.created', 'shipment.status_changed', 'payment.status_changed', 'order.status_changed', 'order.status_aged', 'order.imported'];
private const ALLOWED_CONDITION_TYPES = ['integration', 'shipment_status', 'payment_status', 'order_status', 'days_in_status'];
private const ALLOWED_CONDITION_TYPES = ['integration', 'shipment_status', 'payment_status', 'payment_method', 'order_status', 'days_in_status'];
private const PAYMENT_STATUS_OPTIONS = [
'0' => 'Nieopłacone',
'1' => 'Częściowo opłacone',
@@ -27,6 +27,12 @@ final class AutomationController
private const ALLOWED_RECIPIENTS = ['client', 'client_and_company', 'company'];
private const ALLOWED_RECEIPT_ISSUE_DATE_MODES = ['today', 'order_date', 'payment_date'];
private const ALLOWED_RECEIPT_DUPLICATE_POLICIES = ['skip_if_exists', 'allow_duplicates'];
private const PAYMENT_METHOD_OPTIONS = [
'cod' => 'Platnosc przy odbiorze (COD)',
'transfer' => 'Przelew bankowy',
'online' => 'Karta / platnosc online',
'other' => 'Inna',
];
private const SHIPMENT_STATUS_OPTIONS = [
'registered' => ['label' => 'Przesylka zarejestrowana', 'statuses' => ['created', 'confirmed']],
'ready_for_pickup' => ['label' => 'Przesylka do odbioru', 'statuses' => ['ready_for_pickup']],
@@ -251,6 +257,7 @@ final class AutomationController
'receiptDuplicatePolicies' => self::ALLOWED_RECEIPT_DUPLICATE_POLICIES,
'shipmentStatusOptions' => self::SHIPMENT_STATUS_OPTIONS,
'paymentStatusOptions' => self::PAYMENT_STATUS_OPTIONS,
'paymentMethodOptions' => self::PAYMENT_METHOD_OPTIONS,
'orderStatusOptions' => $this->repository->listActiveOrderStatuses(),
'errorMessage' => $errorMessage !== '' ? $errorMessage : Flash::get('settings.automation.error', ''),
], 'layouts/app');
@@ -275,6 +282,8 @@ final class AutomationController
$value = ['status_keys' => is_array($cond['shipment_status_keys'] ?? null) ? $cond['shipment_status_keys'] : []];
} elseif ($type === 'payment_status') {
$value = ['status_keys' => is_array($cond['payment_status_keys'] ?? null) ? $cond['payment_status_keys'] : []];
} elseif ($type === 'payment_method') {
$value = ['method_keys' => is_array($cond['payment_method_keys'] ?? null) ? $cond['payment_method_keys'] : []];
} elseif ($type === 'order_status') {
$value = ['order_status_codes' => is_array($cond['order_status_codes'] ?? null) ? $cond['order_status_codes'] : []];
} elseif ($type === 'days_in_status') {
@@ -441,6 +450,21 @@ final class AutomationController
return count($statusKeys) > 0 ? ['status_keys' => array_values(array_unique($statusKeys))] : null;
}
if ($type === 'payment_method') {
$keys = $condition['payment_method_keys'] ?? [];
if (!is_array($keys)) {
$keys = [];
}
$allowedKeys = array_keys(self::PAYMENT_METHOD_OPTIONS);
$methodKeys = 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($methodKeys) > 0 ? ['method_keys' => array_values(array_unique($methodKeys))] : null;
}
if ($type === 'order_status') {
$codes = $condition['order_status_codes'] ?? [];
if (!is_array($codes)) {

View File

@@ -3,6 +3,7 @@ declare(strict_types=1);
namespace App\Modules\Automation;
use App\Core\Support\StringHelper;
use App\Modules\Accounting\ReceiptIssueException;
use App\Modules\Accounting\ReceiptRepository;
use App\Modules\Accounting\ReceiptService;
@@ -134,6 +135,9 @@ final class AutomationService
if ($type === 'payment_status') {
return $this->evaluatePaymentStatusCondition($value, $context);
}
if ($type === 'payment_method') {
return $this->evaluatePaymentMethodCondition($value, $order);
}
if ($type === 'order_status') {
return $this->evaluateOrderStatusCondition($value, $context);
}
@@ -216,6 +220,57 @@ final class AutomationService
return in_array($newPaymentStatus, array_map(static fn (mixed $k): string => trim((string) $k), $statusKeys), true);
}
/**
* @param array<string, mixed> $value
* @param array<string, mixed> $order
*/
private function evaluatePaymentMethodCondition(array $value, array $order): bool
{
$methodKeys = is_array($value['method_keys'] ?? null) ? $value['method_keys'] : [];
if ($methodKeys === []) {
return false;
}
$paymentType = trim((string) ($order['external_payment_type_id'] ?? ''));
if ($paymentType === '') {
return false;
}
$upperType = strtoupper($paymentType);
foreach ($methodKeys as $key) {
$match = match ((string) $key) {
'cod' => StringHelper::isCodPayment($paymentType),
'transfer' => str_contains($upperType, 'PRZELEW')
|| str_contains($upperType, 'TRANSFER')
|| str_contains($upperType, 'WIRE'),
'online' => str_contains($upperType, 'CARD')
|| str_contains($upperType, 'ONLINE')
|| str_contains($upperType, 'PAYU')
|| str_contains($upperType, 'PRZELEWY24')
|| str_contains($upperType, 'BLIK')
|| str_contains($upperType, 'TPAY'),
'other' => !StringHelper::isCodPayment($paymentType)
&& !str_contains($upperType, 'PRZELEW')
&& !str_contains($upperType, 'TRANSFER')
&& !str_contains($upperType, 'WIRE')
&& !str_contains($upperType, 'CARD')
&& !str_contains($upperType, 'ONLINE')
&& !str_contains($upperType, 'PAYU')
&& !str_contains($upperType, 'PRZELEWY24')
&& !str_contains($upperType, 'BLIK')
&& !str_contains($upperType, 'TPAY'),
default => false,
};
if ($match) {
return true;
}
}
return false;
}
/**
* @param array<string, mixed> $value
* @param array<string, mixed> $context
@@ -491,7 +546,7 @@ final class AutomationService
$details = $this->orders->findDetails($orderId);
$order = is_array($details['order'] ?? null) ? $details['order'] : [];
$oldStatus = strtolower(trim((string) ($order['external_status_id'] ?? '')));
$oldStatus = strtolower(trim((string) ($order['status_code'] ?? '')));
$actorName = 'Automatyzacja: ' . $ruleName;
$updated = $this->orders->updateOrderStatus($orderId, $statusCode, 'system', $actorName);

View File

@@ -60,7 +60,7 @@ final class OrderStatusAgedService
}
try {
$currentStatus = strtolower(trim((string) ($order['external_status_id'] ?? '')));
$currentStatus = strtolower(trim((string) ($order['status_code'] ?? '')));
$lastChanged = (string) ($order['last_changed'] ?? '');
$actualDays = $lastChanged !== '' ? $this->daysSince($lastChanged) : $days;
@@ -129,12 +129,12 @@ final class OrderStatusAgedService
$placeholders = implode(', ', array_fill(0, count($statusCodes), '?'));
$sql = "SELECT o.id, o.external_status_id, MAX(h.changed_at) AS last_changed
$sql = "SELECT o.id, o.status_code, MAX(h.changed_at) AS last_changed
FROM orders o
INNER JOIN order_status_history h ON h.order_id = o.id
AND LOWER(h.to_status_id) = LOWER(o.external_status_id)
WHERE LOWER(COALESCE(o.external_status_id, '')) IN ({$placeholders})
GROUP BY o.id, o.external_status_id
AND LOWER(h.to_status_id) = LOWER(o.status_code)
WHERE LOWER(COALESCE(o.status_code, '')) IN ({$placeholders})
GROUP BY o.id, o.status_code
HAVING MAX(h.changed_at) <= DATE_SUB(NOW(), INTERVAL ? DAY)
LIMIT " . self::MAX_ORDERS_PER_RULE;