feat(47-shipment-created-automation): immediate shipment automation trigger
Phase 47 complete: - add event shipment.created triggered immediately after shipment creation - add action update_shipment_status with real-change guard and chain-safe emit - update automation UI/options, docs, and PAUL state artifacts
This commit is contained in:
@@ -15,9 +15,9 @@ use Throwable;
|
||||
|
||||
final class AutomationController
|
||||
{
|
||||
private const ALLOWED_EVENTS = ['receipt.created', 'shipment.status_changed'];
|
||||
private const ALLOWED_EVENTS = ['receipt.created', 'shipment.created', 'shipment.status_changed'];
|
||||
private const ALLOWED_CONDITION_TYPES = ['integration', 'shipment_status'];
|
||||
private const ALLOWED_ACTION_TYPES = ['send_email', 'issue_receipt'];
|
||||
private const ALLOWED_ACTION_TYPES = ['send_email', 'issue_receipt', 'update_shipment_status'];
|
||||
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'];
|
||||
@@ -416,6 +416,15 @@ final class AutomationController
|
||||
];
|
||||
}
|
||||
|
||||
if ($type === 'update_shipment_status') {
|
||||
$statusKey = trim((string) ($action['shipment_status_key'] ?? ''));
|
||||
if (!array_key_exists($statusKey, self::SHIPMENT_STATUS_OPTIONS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ['status_key' => $statusKey];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ use App\Modules\Email\EmailSendingService;
|
||||
use App\Modules\Orders\OrdersRepository;
|
||||
use App\Modules\Settings\CompanySettingsRepository;
|
||||
use App\Modules\Settings\ReceiptConfigRepository;
|
||||
use App\Modules\Shipments\DeliveryStatus;
|
||||
use App\Modules\Shipments\ShipmentPackageRepository;
|
||||
use Throwable;
|
||||
|
||||
final class AutomationService
|
||||
@@ -32,7 +34,8 @@ final class AutomationService
|
||||
private readonly OrdersRepository $orders,
|
||||
private readonly CompanySettingsRepository $companySettings,
|
||||
private readonly ReceiptRepository $receipts,
|
||||
private readonly ReceiptConfigRepository $receiptConfigs
|
||||
private readonly ReceiptConfigRepository $receiptConfigs,
|
||||
private readonly ShipmentPackageRepository $shipmentPackages
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -187,6 +190,11 @@ final class AutomationService
|
||||
|
||||
if ($type === 'issue_receipt') {
|
||||
$this->handleIssueReceipt($config, $orderId, $ruleName, $context);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type === 'update_shipment_status') {
|
||||
$this->handleUpdateShipmentStatus($config, $orderId, $ruleName, $context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,6 +363,106 @@ final class AutomationService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $config
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
private function handleUpdateShipmentStatus(array $config, int $orderId, string $ruleName, array $context): void
|
||||
{
|
||||
$statusKey = trim((string) ($config['status_key'] ?? ''));
|
||||
$targetStatus = $this->resolveStatusFromActionKey($statusKey);
|
||||
if ($targetStatus === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$targetPackage = $this->resolveTargetPackage($orderId, $context);
|
||||
if ($targetPackage === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$packageId = (int) ($targetPackage['id'] ?? 0);
|
||||
if ($packageId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$previousStatus = trim((string) ($targetPackage['delivery_status'] ?? DeliveryStatus::UNKNOWN));
|
||||
if ($previousStatus === '') {
|
||||
$previousStatus = DeliveryStatus::UNKNOWN;
|
||||
}
|
||||
if ($previousStatus === $targetStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->shipmentPackages->updateDeliveryStatus($packageId, $targetStatus, null);
|
||||
|
||||
$actorName = 'Automatyzacja: ' . $ruleName;
|
||||
$this->orders->recordActivity(
|
||||
$orderId,
|
||||
'automation_shipment_status_updated',
|
||||
$actorName . ' - zaktualizowano status przesylki',
|
||||
[
|
||||
'package_id' => $packageId,
|
||||
'previous_status' => $previousStatus,
|
||||
'delivery_status' => $targetStatus,
|
||||
'status_key' => $statusKey,
|
||||
],
|
||||
'system',
|
||||
$actorName
|
||||
);
|
||||
|
||||
$this->emitEvent(
|
||||
'shipment.status_changed',
|
||||
$orderId,
|
||||
$context,
|
||||
[
|
||||
'package_id' => $packageId,
|
||||
'provider' => (string) ($targetPackage['provider'] ?? ''),
|
||||
'delivery_status' => $targetStatus,
|
||||
'delivery_status_raw' => '',
|
||||
'previous_status' => $previousStatus,
|
||||
'previous_status_raw' => '',
|
||||
'automation_source' => 'update_shipment_status',
|
||||
'automation_rule' => $ruleName,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function resolveStatusFromActionKey(string $statusKey): ?string
|
||||
{
|
||||
if ($statusKey === '' || !isset(self::SHIPMENT_STATUS_OPTION_MAP[$statusKey])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$mappedStatuses = self::SHIPMENT_STATUS_OPTION_MAP[$statusKey];
|
||||
if ($mappedStatuses === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$candidate = trim((string) $mappedStatuses[0]);
|
||||
if ($candidate === '' || !in_array($candidate, DeliveryStatus::ALL_STATUSES, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function resolveTargetPackage(int $orderId, array $context): ?array
|
||||
{
|
||||
$packageId = (int) ($context['package_id'] ?? 0);
|
||||
if ($packageId > 0) {
|
||||
$package = $this->shipmentPackages->findById($packageId);
|
||||
if (is_array($package) && (int) ($package['order_id'] ?? 0) === $orderId) {
|
||||
return $package;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->shipmentPackages->findLatestByOrderId($orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $order
|
||||
* @param list<array<string, mixed>> $payments
|
||||
|
||||
Reference in New Issue
Block a user