Add machine wash configuration and controls to self-serve module

This commit is contained in:
Jeppe Bundgaard
2026-06-29 15:15:29 +02:00
parent 02b6df5e3b
commit ce8ba88b16
5 changed files with 118 additions and 5 deletions
@@ -360,9 +360,13 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
$allowedServices = $this->normalizeServiceNames(
is_array($metadata['allowed_services'] ?? null) ? (array)$metadata['allowed_services'] : []
);
$machineAvailable = array_key_exists('machine_available', $metadata)
$machineWashEnabled = $this->isMachineWashEnabled();
if (!$machineWashEnabled) {
$allowedServices = $this->withoutMachineService($allowedServices);
}
$machineAvailable = $machineWashEnabled && (array_key_exists('machine_available', $metadata)
? (bool)$metadata['machine_available']
: ($lane->exists() && !empty($lane->relay_machine_id->value()));
: ($lane->exists() && !empty($lane->relay_machine_id->value())));
$allVisibleQuestionsAnswered = array_key_exists('all_visible_questions_answered', $metadata)
? (bool)$metadata['all_visible_questions_answered']
: true;
@@ -408,6 +412,7 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
'events' => $events,
'allowed_services' => $allowedServices,
'machine_available' => $machineAvailable,
'machine_wash_enabled' => $machineWashEnabled,
'all_visible_questions_answered' => $allVisibleQuestionsAnswered,
'allowed' => (bool)$session->allowed->value(),
'config_version_id' => $metadata['config_version_id'] ?? null,
@@ -687,7 +692,12 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
}
}
$machineAvailable = !empty($lane->relay_machine_id->value());
$machineWashEnabled = $this->isMachineWashEnabled();
if (!$machineWashEnabled) {
$allowedServices = $this->withoutMachineService($allowedServices);
}
$machineAvailable = $machineWashEnabled && !empty($lane->relay_machine_id->value());
$allVisibleQuestionsAnswered = true;
foreach ($visibleQuestions as $question) {
if ($question['answer'] === null) {
@@ -726,8 +736,10 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
'tasks' => $visibleTasks,
'allowed_services' => $allowedServices,
'machine_available' => $machineAvailable,
'machine_wash_enabled' => $machineWashEnabled,
'all_visible_questions_answered' => $allVisibleQuestionsAnswered,
'allowed' => $machineAllowed,
'blocked_reason' => !$machineWashEnabled ? 'Machine wash is disabled globally.' : null,
'config_version_id' => $publishedConfigVersionId === null ? null : (int)$publishedConfigVersionId,
'config_source' => $configSource,
'evaluation_trace' => [
@@ -826,6 +838,7 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
'tasks' => $snapshot['tasks'],
'allowed_services' => $snapshot['allowed_services'],
'machine_available' => $snapshot['machine_available'],
'machine_wash_enabled' => $snapshot['machine_wash_enabled'] ?? true,
'all_visible_questions_answered' => $snapshot['all_visible_questions_answered'],
'allowed' => $snapshot['allowed'],
'blocked_reason' => $snapshot['blocked_reason'] ?? null,
@@ -3310,6 +3323,7 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
return [
'allowed_services' => $snapshot['allowed_services'],
'machine_available' => (bool)$snapshot['machine_available'],
'machine_wash_enabled' => (bool)($snapshot['machine_wash_enabled'] ?? true),
'all_visible_questions_answered' => (bool)$snapshot['all_visible_questions_answered'],
'config_version_id' => $snapshot['config_version_id'] ?? null,
'evaluation_trace' => $snapshot['evaluation_trace'] ?? null,
@@ -3608,6 +3622,27 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
));
}
protected function isMachineWashEnabled(): bool
{
try {
return (new selfserve())->config->machine_wash_enabled->isTrue();
} catch (\Throwable) {
return true;
}
}
/**
* @param array<int,string> $services
* @return array<int,string>
*/
protected function withoutMachineService(array $services): array
{
return array_values(array_filter(
$this->normalizeServiceNames($services),
static fn(string $service): bool => $service !== selfserve_lane_services::MACHINE->name
));
}
protected function taskUsesMachineControls(array $task): bool
{
if (in_array(selfserve_lane_services::MACHINE->name, $this->normalizeServiceNames($this->normalizeJsonArray($task['services'] ?? null)), true)) {
@@ -0,0 +1,29 @@
<?php
namespace modules\selfserve\config;
use Exception;
use traits\module_config_variable;
class selfserve_machine_wash_enabled_c
{
use module_config_variable;
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'selfserve',
'machine_wash_enabled',
'bool',
true,
null,
'Whether machine wash is available in customer-facing self-serve flows',
'1',
false,
true
);
}
}
@@ -2,12 +2,14 @@
namespace modules\selfserve;
require_once WD . '/modules/selfserve/config/selfserve_enabled_c.php';
require_once WD . '/modules/selfserve/config/selfserve_machine_wash_enabled_c.php';
require_once WD . '/modules/selfserve/config/selfserve_minute_product_c.php';
require_once WD . '/modules/selfserve/config/selfserve_machine_wash_minutes_included_c.php';
require_once WD . '/modules/selfserve/config/selfserve_dynamic_image_size_c.php';
use modules\selfserve\config\selfserve_dynamic_image_size_c;
use modules\selfserve\config\selfserve_enabled_c;
use modules\selfserve\config\selfserve_machine_wash_enabled_c;
use modules\selfserve\config\selfserve_machine_wash_minutes_included_c;
use modules\selfserve\config\selfserve_minute_product_c;
use traits\module_config_t;
@@ -21,6 +23,11 @@ class selfserve_c
* @var selfserve_enabled_c $enabled
*/
public selfserve_enabled_c $enabled;
/**
* Whether machine wash is available in customer-facing self-serve flows
* @var selfserve_machine_wash_enabled_c $machine_wash_enabled
*/
public selfserve_machine_wash_enabled_c $machine_wash_enabled;
/**
* The product ID used for minute-based self-serve billing
* @var selfserve_minute_product_c $minute_product
@@ -42,11 +49,13 @@ class selfserve_c
$this->setupConfig('selfserve');
$this->allowUpdate([
selfserve_enabled_c::class,
selfserve_machine_wash_enabled_c::class,
selfserve_minute_product_c::class,
selfserve_machine_wash_minutes_included_c::class,
selfserve_dynamic_image_size_c::class
]);
$this->enabled = new selfserve_enabled_c();
$this->machine_wash_enabled = new selfserve_machine_wash_enabled_c();
$this->minute_product = new selfserve_minute_product_c();
$this->machine_wash_minutes_included = new selfserve_machine_wash_minutes_included_c();
$this->dynamic_image_size = new selfserve_dynamic_image_size_c();
@@ -716,6 +716,12 @@ class moduleSelfServeRoute
// Merge services (if any)
$merge_services((array)$t->services->value());
}
if (!$this->isMachineWashEnabled()) {
$allowed_services = array_values(array_filter(
$allowed_services,
static fn(string $service): bool => $service !== selfserve_lane_services::MACHINE->name
));
}
// Persist on lane cache (overwrites previous allowed services)
try {
$relay_sync = $lane->setAllowedServicesFromVisibleTasks($allowed_services);
@@ -1807,12 +1813,21 @@ class moduleSelfServeRoute
protected function isSelfServeModuleEnabled(): bool
{
try {
return (bool)(new selfserve())->config->enabled->getVariableValue();
return (new selfserve())->config->enabled->isTrue();
} catch (\Throwable) {
return false;
}
}
protected function isMachineWashEnabled(): bool
{
try {
return (new selfserve())->config->machine_wash_enabled->isTrue();
} catch (\Throwable) {
return true;
}
}
protected function canCustomerUseSelfServeLane(selfserve_lane $lane, int $customer_number): bool
{
return $customer_number > 0
@@ -167,12 +167,19 @@ it('wires the all-in-one self-serve studio replacement endpoints', function ():
expect($studioGraph)->toContain('resolved');
});
it('wires machine wash included minutes into self-serve module config', function (): void {
it('wires machine wash controls into self-serve module config', function (): void {
$selfserveConfig = file_get_contents(app_path('modules/selfserve/selfserve_c.php'));
$machineWashEnabledConfig = file_get_contents(app_path('modules/selfserve/config/selfserve_machine_wash_enabled_c.php'));
expect($selfserveConfig)->not->toBeFalse();
expect($selfserveConfig)->toContain('selfserve_machine_wash_minutes_included_c');
expect($selfserveConfig)->toContain('machine_wash_minutes_included');
expect($selfserveConfig)->toContain('selfserve_machine_wash_enabled_c');
expect($selfserveConfig)->toContain('machine_wash_enabled');
expect($machineWashEnabledConfig)->not->toBeFalse()
->and($machineWashEnabledConfig)->toContain("'machine_wash_enabled'")
->and($machineWashEnabledConfig)->toContain("'bool'")
->and($machineWashEnabledConfig)->toContain('true');
});
it('keeps legacy self-serve CRUD routes syncing canonical drafts', function (): void {
@@ -409,6 +416,24 @@ it('filters machine button tasks out of self-serve snapshots when MACHINE is not
->and($washFlow)->toContain("\$task['dynamic_images_vehicle_type'] ?? null");
});
it('applies the global machine wash toggle before exposing machine tasks or services', function (): void {
$washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
expect($washFlow)->not->toBeFalse()
->and($washFlow)->toContain('$machineWashEnabled = $this->isMachineWashEnabled();')
->and($washFlow)->toContain('$allowedServices = $this->withoutMachineService($allowedServices);')
->and($washFlow)->toContain('$machineAvailable = $machineWashEnabled && !empty($lane->relay_machine_id->value());')
->and($washFlow)->toContain("'machine_wash_enabled' => \$machineWashEnabled")
->and($washFlow)->toContain('Machine wash is disabled globally.')
->and($washFlow)->toContain('->config->machine_wash_enabled->isTrue()');
expect($moduleSelfServeRoute)->not->toBeFalse()
->and($moduleSelfServeRoute)->toContain('if (!$this->isMachineWashEnabled())')
->and($moduleSelfServeRoute)->toContain('selfserve_lane_services::MACHINE->name')
->and($moduleSelfServeRoute)->toContain('->config->machine_wash_enabled->isTrue()');
});
it('keeps self-serve force stop distinct from normal STOP relay and gate behavior', function (): void {
$washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));
$sessionObject = file_get_contents(app_path('objects/selfserve_wash_sessions_o.php'));