Add support for selfserve_enabled lanes and synchronize behavior across tasks, sessions, and projections

- Introduced `selfserve_enabled` property for `department_lanes` with schema update, object properties, and associated methods/tests.
- Enhanced `selfserve_wash_flow` and session logic to respect lane self-serve settings, including block handling and task filtering.
- Updated API routes and Studio Graph projections to include `selfserve_enabled` in payloads and progress callbacks.
- Added unit tests for session statuses, lane configuration, and blocking behavior due to disabled self-serve settings.
This commit is contained in:
Jeppe Bundgaard
2026-04-29 17:28:42 +02:00
parent 5875371d13
commit 8bf957b273
18 changed files with 924 additions and 101 deletions
@@ -14,6 +14,11 @@ class selfserve_wash_sessions_o extends db
{
use db_object_t;
public const TERMINAL_STATUSES = [
'COMPLETED',
'FORCE_STOPPED',
];
public object_property $lane_id;
public object_property $department_id;
public object_property $machine_type_id;
@@ -105,6 +110,25 @@ class selfserve_wash_sessions_o extends db
$this->status->set($status->value);
}
public static function isTerminalStatus(?string $status): bool
{
return in_array(strtoupper(trim((string)$status)), self::TERMINAL_STATUSES, true);
}
public static function terminalStatusSqlList(): string
{
return "'" . implode("','", array_map(
static fn(string $status): string => str_replace("'", "''", $status),
self::TERMINAL_STATUSES
)) . "'";
}
public function isOpen(): bool
{
return $this->completed_at->value() === null
&& !self::isTerminalStatus((string)$this->status->value());
}
public function markRelayEnabled(): void
{
$now = date('Y-m-d H:i:s');
@@ -167,7 +191,11 @@ class selfserve_wash_sessions_o extends db
if ($customerNumber !== null) {
$filters['customer_number'] = $customerNumber;
}
$rows = $this->getFieldsWhere($filters, ['id']);
$rows = $this->getFieldsWhere($filters, ['id', 'status']);
$rows = array_values(array_filter(
$rows,
static fn(array $row): bool => !self::isTerminalStatus($row['status'] ?? null)
));
if ($rows === []) {
return $this;
}
@@ -187,7 +215,11 @@ class selfserve_wash_sessions_o extends db
if ($customerNumber !== null) {
$filters['customer_number'] = $customerNumber;
}
$rows = $this->getFieldsWhere($filters, ['id']);
$rows = $this->getFieldsWhere($filters, ['id', 'status']);
$rows = array_values(array_filter(
$rows,
static fn(array $row): bool => !self::isTerminalStatus($row['status'] ?? null)
));
if ($rows === []) {
return $this;
}
@@ -232,6 +264,7 @@ class selfserve_wash_sessions_o extends db
'order_id' => $this->order_id->value() === null ? null : (int)$this->order_id->value(),
'completed_at' => $this->completed_at->value() === null ? null : (string)$this->completed_at->value(),
'metadata' => (array)($this->metadata_json->value() ?? []),
'open' => $this->isOpen(),
'created_at' => (string)$this->created_at->value(),
'updated_at' => $this->updated_at->value() === null ? null : (string)$this->updated_at->value(),
];
@@ -246,16 +279,17 @@ class selfserve_wash_sessions_o extends db
try {
$start = new DateTime((string)$startAt);
$now = new DateTime();
$endAt = $this->completed_at->value() ?? date('Y-m-d H:i:s');
$end = new DateTime((string)$endAt);
} catch (\Throwable) {
return 0;
}
if ($start > $now) {
if ($start > $end) {
return 0;
}
$diff = $start->diff($now);
$diff = $start->diff($end);
return (int)(($diff->days * 24 * 60) + ($diff->h * 60) + $diff->i);
}
}