One large-type page for the front desk (Hotel Reservation System > Rezeption, first menu entry): - today's arrivals, departures, guests in house and overdue arrivals, each guest as a card with big room number, name, people, nights, room type and booking source - one obvious button per guest: Einchecken / Auschecken (and "Früher abreisen"); after each action a big confirmation with "Rückgängig" instead of confirm dialogs; Post/Redirect/Get so reload never repeats - uses HotelBookingDetail::changeStatus(), so status history and hooks (casposbridge posts the nights) run as in the order page; check-out does not require payment in QloApps because guests pay at the POS - shows the CASPOS state per guest; other days are read-only - tiles to Belegungsplan and Neue Reservierung (Book Now) - employee profile "Rezeption" that only sees Rezeption, Belegungsplan, Book Now, orders, customers, carts and guest registration cards - docker/test-frontdesk.php: 21 checks with throw-away guests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
6.9 KiB
PHP
121 lines
6.9 KiB
PHP
<?php
|
|
/**
|
|
* Checks modules/frontdesk with throw-away guests and writes an HTML preview.
|
|
* docker compose ... exec -u www-data app php docker/test-frontdesk.php [preview-file]
|
|
* casposbridge is switched off during the test so no POS bookings are made.
|
|
*/
|
|
if (PHP_SAPI !== 'cli') {
|
|
exit;
|
|
}
|
|
define('_PS_ADMIN_DIR_', __DIR__);
|
|
require __DIR__.'/../config/config.inc.php';
|
|
require_once _PS_MODULE_DIR_.'hotelreservationsystem/define.php';
|
|
require_once _PS_MODULE_DIR_.'frontdesk/classes/FrontDeskPage.php';
|
|
|
|
$preview = isset($argv[1]) ? $argv[1] : null;
|
|
$fail = 0;
|
|
function check($c, $m)
|
|
{
|
|
global $fail;
|
|
echo ($c ? ' [ok] ' : ' [FAIL] ').$m.PHP_EOL;
|
|
$fail += $c ? 0 : 1;
|
|
}
|
|
if (!Module::isInstalled('frontdesk')) {
|
|
check(Module::getInstanceByName('frontdesk')->install(), 'module installed');
|
|
}
|
|
check((bool)Tab::getIdFromClassName('AdminFrontDesk'), 'menu "Rezeption" exists');
|
|
$idProfile = (int)Db::getInstance()->getValue('SELECT `id_profile` FROM `'._DB_PREFIX_.'profile_lang` WHERE `name` = \'Rezeption\'');
|
|
check($idProfile > 0, 'profile "Rezeption" exists');
|
|
$visible = Db::getInstance()->executeS('SELECT t.`class_name` FROM `'._DB_PREFIX_.'access` a JOIN `'._DB_PREFIX_.'tab` t ON t.`id_tab` = a.`id_tab` WHERE a.`id_profile` = '.$idProfile.' AND a.`view` = 1');
|
|
$visible = array_column($visible, 'class_name');
|
|
check(in_array('AdminFrontDesk', $visible, true) && in_array('AdminOccupancyPlan', $visible, true), 'profile sees Rezeption and Belegungsplan');
|
|
check(!in_array('AdminModules', $visible, true) && !in_array('AdminProducts', $visible, true) && !in_array('AdminEmployees', $visible, true), 'profile does not see modules, products, employees');
|
|
check(count($visible) <= 15, 'profile sees only '.count($visible).' pages');
|
|
|
|
$cbEnabled = Configuration::get('CASPOSBRIDGE_ENABLED');
|
|
Configuration::updateValue('CASPOSBRIDGE_ENABLED', 0);
|
|
|
|
$db = Db::getInstance();
|
|
$rooms = $db->executeS('SELECT `id`, `id_product`, `room_num` FROM `'._DB_PREFIX_.'htl_room_information` ORDER BY `id` DESC LIMIT 5');
|
|
$d = function ($n) { return date('Y-m-d', strtotime(($n >= 0 ? '+' : '').$n.' day')); };
|
|
$plan = array(
|
|
array($rooms[0], 0, 3, 1, 'Erika', 'Mustermann', 2, 0), // arrives today
|
|
array($rooms[1], 0, 1, 1, 'Hans', 'Schmidt', 1, 0), // arrives today
|
|
array($rooms[2], -3, 0, 2, 'Gisela', 'Meier', 2, 1), // leaves today
|
|
array($rooms[3], -1, 4, 2, 'Peter', 'Wagner', 2, 0), // in house
|
|
array($rooms[4], -1, 2, 1, 'Karl', 'Spät', 1, 0), // overdue
|
|
);
|
|
$ids = array();
|
|
$customers = array();
|
|
foreach ($plan as $p) {
|
|
list($room, $from, $to, $status, $fn, $ln, $ad, $ch) = $p;
|
|
$c = new Customer();
|
|
$c->firstname = $fn;
|
|
$c->lastname = $ln;
|
|
$c->email = 'fd-test-'.uniqid().'@hotel.test';
|
|
$c->passwd = Tools::passwdGen(16);
|
|
$c->add();
|
|
$customers[] = (int)$c->id;
|
|
$db->insert('htl_booking_detail', array(
|
|
'id_product' => (int)$room['id_product'], 'id_order' => 0, 'id_order_detail' => 1, 'id_cart' => 1, 'id_room' => (int)$room['id'],
|
|
'id_hotel' => 1, 'id_customer' => (int)$c->id, 'booking_type' => 1, 'id_status' => $status,
|
|
'check_in' => $status === 2 ? $d($from).' 15:10:00' : '0000-00-00 00:00:00', 'check_out' => '0000-00-00 00:00:00',
|
|
'planned_check_out' => $d($to).' 11:00:00', 'date_from' => $d($from).' 00:00:00', 'date_to' => $d($to).' 00:00:00',
|
|
'total_price_tax_excl' => 100, 'total_price_tax_incl' => 107, 'total_paid_amount' => 0, 'is_back_order' => 0,
|
|
'hotel_name' => 'Test', 'room_type_name' => 'Doppelzimmer', 'room_num' => $room['room_num'], 'adults' => $ad, 'children' => $ch,
|
|
'city' => 'Annweiler', 'country' => 'Deutschland', 'phone' => '0', 'email' => $c->email,
|
|
'check_in_time' => '15:00', 'check_out_time' => '11:00', 'date_add' => date('Y-m-d H:i:s'), 'date_upd' => date('Y-m-d H:i:s'),
|
|
));
|
|
$ids[] = (int)$db->Insert_ID();
|
|
}
|
|
$mine = function (array $rows) use ($ids) {
|
|
return array_values(array_filter($rows, function ($r) use ($ids) { return in_array((int)$r['id'], $ids, true); }));
|
|
};
|
|
|
|
try {
|
|
$s = new FrontDeskService();
|
|
$today = $d(0);
|
|
check(count($mine($s->arrivals($today))) === 2, 'arrivals today: 2');
|
|
check(count($mine($s->departures($today))) === 1, 'departures today: 1');
|
|
check(count($mine($s->inHouse($today))) === 1, 'in house: 1');
|
|
check(count($mine($s->overdue($today))) === 1, 'overdue: 1');
|
|
|
|
list($ok, $msg) = $s->act($ids[0], 'checkin');
|
|
check($ok && strpos($msg, 'Erika Mustermann') !== false, 'check-in: '.$msg);
|
|
check((int)$db->getValue('SELECT id_status FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id = '.$ids[0]) === 2, 'status is checked in');
|
|
list($ok) = $s->act($ids[0], 'checkin');
|
|
check(!$ok, 'second check-in is refused');
|
|
list($ok, $msg) = $s->act($ids[0], 'undo_checkin');
|
|
check($ok && (int)$db->getValue('SELECT id_status FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id = '.$ids[0]) === 1, 'undo check-in: '.$msg);
|
|
list($ok, $msg) = $s->act($ids[2], 'checkout');
|
|
check($ok, 'check-out without payment in QloApps (paid at POS): '.$msg);
|
|
list($ok, $msg) = $s->act($ids[2], 'undo_checkout');
|
|
check($ok, 'undo check-out: '.$msg);
|
|
check($db->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'htl_booking_status_history` WHERE id_htl_booking = '.$ids[0]) >= 2, 'status history written');
|
|
|
|
$s->act($ids[1], 'checkin');
|
|
$page = new FrontDeskPage($s, $today, 'index.php?controller=AdminFrontDesk&token=x', array('planUrl' => '#plan', 'reserveUrl' => '#reserve'));
|
|
$html = $page->render(array('ok' => true, 'msg' => 'Hans Schmidt (Zimmer '.$rooms[1]['room_num'].') ist eingecheckt.', 'undo' => array($ids[1], 'undo_checkin')));
|
|
check(strpos($html, 'Einchecken') !== false && strpos($html, 'Auschecken') !== false, 'big check-in and check-out buttons');
|
|
check(strpos($html, 'Rückgängig') !== false, 'undo button after an action');
|
|
check(strpos($html, 'Noch nicht angekommen') !== false, 'overdue section shown');
|
|
$future = (new FrontDeskPage($s, $d(1), 'x', array()))->render();
|
|
check(strpos($future, 'name="fd_action"') === false, 'no action buttons on other days');
|
|
if ($preview) {
|
|
file_put_contents($preview, '<!doctype html><meta charset="utf-8"><title>Rezeption Vorschau</title>'
|
|
.'<body style="font-family:Open Sans,Arial,sans-serif;margin:16px;background:#eff1f2">'.$html.'</body>');
|
|
echo "Preview: $preview\n";
|
|
}
|
|
} catch (Throwable $e) {
|
|
check(false, 'unexpected '.get_class($e).': '.$e->getMessage().' @ '.basename($e->getFile()).':'.$e->getLine());
|
|
} finally {
|
|
$db->delete('htl_booking_status_history', '`id_htl_booking` IN ('.implode(',', $ids).')');
|
|
$db->delete('htl_booking_detail', '`id` IN ('.implode(',', $ids).')');
|
|
foreach ($customers as $idc) {
|
|
(new Customer($idc))->delete();
|
|
}
|
|
Configuration::updateValue('CASPOSBRIDGE_ENABLED', $cbEnabled);
|
|
}
|
|
echo $fail ? "$fail check(s) FAILED\n" : "All checks passed\n";
|
|
exit($fail ? 1 : 0);
|