139 lines
5.0 KiB
PHP
139 lines
5.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
function loadRoomPlanningSettings(PDO $pdo): array
|
|
{
|
|
$defaults = [
|
|
'single' => 0,
|
|
'double' => 0,
|
|
'multi' => 0,
|
|
];
|
|
$statement = $pdo->query(
|
|
"SELECT setting_key, price FROM pricing_settings
|
|
WHERE setting_key IN ('room_count_single', 'room_count_double', 'room_count_multi')"
|
|
);
|
|
foreach ($statement->fetchAll(PDO::FETCH_KEY_PAIR) as $key => $value) {
|
|
$type = str_replace('room_count_', '', (string) $key);
|
|
if (array_key_exists($type, $defaults)) {
|
|
$defaults[$type] = max(0, (int) $value);
|
|
}
|
|
}
|
|
|
|
return $defaults;
|
|
}
|
|
|
|
function normalizedRoommateName(string $name): string
|
|
{
|
|
return mb_strtolower(preg_replace('/\\s+/u', ' ', trim($name)) ?? '', 'UTF-8');
|
|
}
|
|
|
|
function buildRoomPlan(array $participants, array $roomCounts): array
|
|
{
|
|
$rooms = [];
|
|
$warnings = [];
|
|
$unassigned = [];
|
|
$available = [
|
|
'single' => max(0, (int) ($roomCounts['single'] ?? 0)),
|
|
'double' => max(0, (int) ($roomCounts['double'] ?? 0)),
|
|
'multi' => max(0, (int) ($roomCounts['multi'] ?? 0)),
|
|
];
|
|
$roomNumber = ['single' => 0, 'double' => 0, 'multi' => 0];
|
|
$assigned = [];
|
|
$blocked = [];
|
|
$byName = [];
|
|
|
|
foreach ($participants as &$participant) {
|
|
$participant['roommate_key'] = normalizedRoommateName((string) ($participant['roommate_requests'] ?? ''));
|
|
$participant['name_key'] = normalizedRoommateName((string) $participant['name']);
|
|
$byName[$participant['name_key']][] = $participant;
|
|
}
|
|
unset($participant);
|
|
|
|
$addRoom = static function (string $type, array $members) use (&$rooms, &$roomNumber, &$available, &$assigned): bool {
|
|
if (($available[$type] ?? 0) < 1) {
|
|
return false;
|
|
}
|
|
$roomNumber[$type]++;
|
|
$capacity = $type === 'single' ? 1 : ($type === 'double' ? 2 : 4);
|
|
$rooms[] = [
|
|
'type' => $type,
|
|
'number' => $roomNumber[$type],
|
|
'capacity' => $capacity,
|
|
'members' => $members,
|
|
];
|
|
$available[$type]--;
|
|
foreach ($members as $member) {
|
|
$assigned[(string) $member['id']] = true;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Mutual wishes are handled first, but only when both participants requested the same room type.
|
|
foreach ($participants as $participant) {
|
|
$id = (string) $participant['id'];
|
|
if (isset($assigned[$id]) || $participant['roommate_key'] === '') {
|
|
continue;
|
|
}
|
|
foreach ($byName[$participant['roommate_key']] ?? [] as $requested) {
|
|
$requestedId = (string) $requested['id'];
|
|
if ($requestedId === $id || isset($assigned[$requestedId])) {
|
|
continue;
|
|
}
|
|
$mutual = $requested['roommate_key'] === $participant['name_key'];
|
|
$sameType = $requested['room_type'] === $participant['room_type'];
|
|
if (!$mutual || !$sameType) {
|
|
continue;
|
|
}
|
|
if (!$addRoom((string) $participant['room_type'], [$participant, $requested])) {
|
|
$warnings[] = sprintf(
|
|
'Der gegenseitige Zimmerwunsch von %s und %s konnte nicht erfüllt werden: kein %s-Zimmer verfügbar.',
|
|
$participant['name'],
|
|
$requested['name'],
|
|
$participant['room_type'] === 'double' ? 'Doppel' : 'Mehrbett'
|
|
);
|
|
$unassigned[$id] = $participant;
|
|
$unassigned[$requestedId] = $requested;
|
|
$blocked[$id] = true;
|
|
$blocked[$requestedId] = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (['single', 'double', 'multi'] as $type) {
|
|
$waiting = [];
|
|
foreach ($participants as $participant) {
|
|
$id = (string) $participant['id'];
|
|
if (!isset($assigned[$id]) && !isset($blocked[$id]) && $participant['room_type'] === $type) {
|
|
$waiting[] = $participant;
|
|
}
|
|
}
|
|
$capacity = $type === 'single' ? 1 : ($type === 'double' ? 2 : 4);
|
|
while ($waiting !== []) {
|
|
$group = array_splice($waiting, 0, $capacity);
|
|
if (!$addRoom($type, $group)) {
|
|
foreach ($group as $participant) {
|
|
$unassigned[(string) $participant['id']] = $participant;
|
|
}
|
|
$warnings[] = sprintf(
|
|
'%d Teilnehmer mit Wunsch %s konnten keinem verfügbaren Zimmer zugeordnet werden.',
|
|
count($group),
|
|
$type === 'single' ? 'Einzelzimmer' : ($type === 'double' ? 'Doppelzimmer' : 'Mehrbettzimmer')
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($unassigned as $participant) {
|
|
$warnings[] = sprintf('Nicht eingeplant: %s (%s).', $participant['name'], $participant['email']);
|
|
}
|
|
|
|
return [
|
|
'rooms' => $rooms,
|
|
'warnings' => array_values(array_unique($warnings)),
|
|
'unassigned' => array_values($unassigned),
|
|
'available' => $available,
|
|
];
|
|
}
|