diff --git a/common/components/StopWatch.php b/common/components/StopWatch.php new file mode 100644 index 0000000..23c7ecb --- /dev/null +++ b/common/components/StopWatch.php @@ -0,0 +1,72 @@ +start(); +// } + public function __construct() + { + $this->start(); + } + + + public function start() + { + $this->start = $this->time(); + } + + public function split() + { + $this->splits[] = $this->time(); + return $this->getSplit(); + } + + public function stop() + { + $this->stop = $this->time(); + } + + function time() + { + return time(); + } + + function diff($start, $end) + { + if (!isset($start) || !isset($end)) { + return -1; + } + return $end - $start; + } + + function getSplit() + { + $lastSplit = null; + if (isset($this->stop)) { + $lastSplit = $this->stop; + } else { + $count = count($this->splits); + if ($count > 0) { + $lastSplit = $this->splits[$count - 1]; + } + } + return $this->diff($this->start, $lastSplit); + } + function getTotal(){ + return $this->diff($this->start, $this->stop); + + } + + +} diff --git a/common/manager/DoorManager.php b/common/manager/DoorManager.php index 091f904..7affbc8 100644 --- a/common/manager/DoorManager.php +++ b/common/manager/DoorManager.php @@ -4,26 +4,15 @@ namespace common\manager; use common\components\DateUtil; use common\components\Helper; +use common\components\StopWatch; use common\models\Card; -use common\models\CardEventRegistrationForm; -use common\models\Customer; use common\models\DoorLog; use common\models\DoorLogForTest; -use common\models\Event; -use common\models\EventRegistration; use common\models\Log; -use common\models\MobileDevice; use common\models\Ticket; -use customerapi\models\available\EventInterval; -use customerapi\models\registrations\EventRegistrationAvailable; -use customerapi\models\details\EventRegistrationView; -use Exception; -use Yii; +use frontend\models\KeyToggleForm; use yii\base\BaseObject; -use yii\db\ActiveRecord; -use yii\db\Query; use yii\web\BadRequestHttpException; -use yii\web\NotFoundHttpException; use yii\web\ServerErrorHttpException; /** @@ -46,219 +35,262 @@ class DoorManager extends BaseObject * @throws BadRequestHttpException * @throws \yii\base\InvalidConfigException */ - public function move($cardNumber, $device, $direction, $createdAt = null, $date = null ) + public function move($cardNumber, $device, $direction, $createdAt = null, $date = null) { - if ( isset($createdAt)){ - $createdAt = DateUtil::parseDateTime($createdAt); - }else{ - $createdAt = DateUtil::utcDateTime(); - } + try { + \Yii::$app->db->beginTransaction(); - $createdAtStr = DateUtil::formatDateTimeUtc($createdAt); + $stopWatch = new StopWatch(); + \Yii::error("Started"); + if (isset($createdAt)) { + $createdAt = DateUtil::parseDateTime($createdAt); + } else { + $createdAt = DateUtil::utcDateTime(); + } - if ( isset($date)){ - $date = DateUtil::parseDateTime($date); - }else{ - $date = DateUtil::utcDate(); - } + $createdAtStr = DateUtil::formatDateTimeUtc($createdAt); - $dateStr = DateUtil::formatDateUtc($date); + if (isset($date)) { + $date = DateUtil::parseDateTime($date); + } else { + $date = DateUtil::utcDate(); + } - $doorLog = new DoorLog(); - $doorLog->direction = $direction; - $doorLog->source_app = $device; - $doorLog->created_at = $createdAtStr; + $dateStr = DateUtil::formatDateUtc($date); - /** - * emergency- no card needed - */ - if ($direction == DoorLog::$DIRECTION_ALL_EMERGENCY) { - $doorLog->save(false); - Log::log( - [ - 'type' => Log::$TYPE_INFO, - 'message' => 'Ajtó nyitás: vészhelyzet', - 'id_door_log' => $doorLog->id_door_log - ] - ); - \Yii::$app->response->statusCode = 204; - return; - } + $doorLog = new DoorLog(); + $doorLog->direction = $direction; + $doorLog->source_app = $device; + $doorLog->created_at = $createdAtStr; - $card = Card::readCard(Helper::fixAsciiChars($cardNumber)); - - /* - * in any other cases, the door needs a card - */ - if (!isset($card)) { - throw new BadRequestHttpException('Card not found with number: ' . $cardNumber); - } - - $doorLog->id_card = $card->id_card; - $doorLog->card_flag = $card->flag; - /** - * if the card type is employee, neither customer nor ticket is needed. - * Free to enter/leave - */ - if ($card->type == Card::TYPE_EMPLOYEE) { - $doorLog->save(false); - \Yii::$app->response->statusCode = 204; - return; - } - - //otherwise ticket is required - $activeTickets = Ticket::readActive($card, clone $date); - \Yii::error('active ticket count:' . count($activeTickets)); - $ticket = null; - if (isset($activeTickets) && count($activeTickets) > 0) { - $ticket = $activeTickets[0]; - } - $doorLog->id_ticket_current = $ticket->id_ticket; - - if (!isset($ticket)) { - throw new BadRequestHttpException("No active ticket found for:" . $card->number); - } - \Yii::error("active ticket: " . $ticket->id_ticket); - - // customer is also required - $customer = $card->customer; - if (!isset($customer)) { - throw new BadRequestHttpException("Customer not found for:" . $card->number); - } - - $doorLog->id_customer = $customer->id_customer; - - // save the door log - $doorLog->save(false); - - - // if direction is in - if ($direction == DoorLog::$DIRECTION_IN || $direction == DoorLog::$DIRECTION_IN_WITHOUT_MOVE) { - $countDoorLogsForTicketSince = $this->getCountDoorLogsForTicketSince($ticket->id_ticket, DateUtil::utcDate( clone $date)); - // if the current event is the first door log today - if ($countDoorLogsForTicketSince == 1) { - // increase the ticket usage count with 1 - $usageCount = $ticket->usage_count; - $ticket->usage_count += 1; - $ticket->save(false); + /** + * emergency- no card needed + */ + if ($direction == DoorLog::$DIRECTION_ALL_EMERGENCY) { + $doorLog->save(false); Log::log( [ - 'type' => Log::$TYPE_TICKET_USAGE_FIRST, - 'message' => 'Bérlet használat(előtte: ' . $usageCount . ' -> utána: ' . $ticket->usage_count, - 'id_ticket' => $ticket->id_ticket, + 'type' => Log::$TYPE_INFO, + 'message' => 'Ajtó nyitás: vészhelyzet', 'id_door_log' => $doorLog->id_door_log ] ); - } else { - - // we have already a door log for today, other than this - // Now we split the day into 3hour intervalls, starting with the createdAt value of the first event. - // If the actual event happens in an interval, in which still now doorlog event happend, we increase - // the usage count with 1 - // 3 óránként 1-et levonunk - - $startOfDay = DateUtil::utcDate(clone $date); - $startOfTomorrow = DateUtil::tomorrowStart(clone $date); - - $allDoorLogToday = DoorLog::find() - ->andWhere(['>=', 'door_log.created_at', DateUtil::formatDateUtc($startOfDay)]) - ->andWhere(['<', 'door_log.created_at', DateUtil::formatDateUtc($startOfTomorrow)]) - ->andWhere(['id_ticket_current' => $ticket->id_ticket]) - ->andWhere(['in', 'direction', [DoorLog::$DIRECTION_IN_WITHOUT_MOVE, DoorLog::$DIRECTION_IN]]) - ->orderBy(['door_log.created_at' => SORT_ASC]) - ->all(); - - \Yii::error("new door log: ".$doorLog->id_door_log.";".$doorLog->created_at.";".$doorLog->type); - foreach ($allDoorLogToday as $log){ - \Yii::error("all log: ".$log->id_door_log.";".$log->created_at.";".$log->type); - } - - $firstInToday = $allDoorLogToday[0]; - - if (isset($firstInToday)) { - - $firstEntryDateTimeToday = DateUtil::parseDateTime($firstInToday->created_at); - - $interval = \DateInterval::createFromDateString('3 hours'); - $daterange = new \DatePeriod($firstEntryDateTimeToday, $interval ,$startOfTomorrow); - - $intervals = []; - $intervalStart = null; - foreach($daterange as $intervalEnd){ - if ( isset($intervalStart)){ - $intervals[] = $this->createTicketUsageInterval($intervalStart,$intervalEnd,$allDoorLogToday,$doorLog); - } - $intervalStart = clone $intervalEnd; - } - - if ( $intervalStart < $startOfTomorrow ){ - $intervals[] = $this->createTicketUsageInterval($intervalStart,$startOfTomorrow,$allDoorLogToday,$doorLog); - } - - $activeInterval = $this->getActiveInterval($intervals,$createdAt); - if ( !isset($activeInterval)){ - throw new ServerErrorHttpException("Active Interval not found"); - } - - $logCountInActiveInterval = count($activeInterval['logs']); - - if ( $logCountInActiveInterval == 1){ - $ticket->usage_count = $ticket->usage_count+1; - $ticket->save(false); - } - } - -// select min(created_at) + INTERVAL (3 * FLOOR( ( ( HOUR( TIMEDIFF( min(created_at) , now() ) ) /3 ) ) ) ) hour as last_date -// into @p_from -// from door_log -// where created_at > CURDATE() and id_customer is not null and id_ticket_current = NEW.id_ticket_current and ( direction = 7 or direction = 3); - - -// select count(*) into @p_count_all_2 from door_log where created_at >= @p_from and id_ticket_current = New.id_ticket_current and ( direction = 7 or direction = 3); - -// INSERT INTO devlog ( msg) values(CONCAT( 'Bel�p�sek sz�ma az aktu�lis 3 �r�s intervalumban: ', @p_count_all_2) ); - - -// IF @p_count_all_2 = 1 -// THEN -// INSERT INTO devlog ( msg) values( 'Az aktu�lis intervallumban ez az els? bel�p�s, usage_count n�vel�se' ); -// -// select usage_count, max_usage_count into @p_usage_count ,@p_max_usage_count from ticket where id_ticket = NEW.id_ticket_current; -// -// update ticket set usage_count = usage_count +1 where id_ticket = New.id_ticket_current; -// -// INSERT INTO log (type,message, app, id_ticket, id_door_log,created_at, updated_at) -// values( -// 40, concat('B�rlet haszn�lat/egy nap tobbszori (elotte: ',@p_usage_count, ' > utana: ' , @p_usage_count +1 , ' max: ', @p_max_usage_count, ')' ), ' trigger_inc_ticket',New.id_ticket_current, New.id_door_log,now(),now()); -// END IF; + \Yii::$app->response->statusCode = 204; + \Yii::error("emergency event in sec " . $stopWatch->split()); + \Yii::$app->db->transaction->commit(); + return; } -// + $card = Card::readCard(Helper::fixAsciiChars($cardNumber)); + \Yii::error("card loaded in sec " . $stopWatch->split()); -// -// + /* + * in any other cases, the door needs a card + */ + if (!isset($card)) { + throw new BadRequestHttpException('Card not found with number: ' . $cardNumber); + } - \Yii::error("finished"); + $doorLog->id_card = $card->id_card; + $doorLog->card_flag = $card->flag; + /** + * if the card type is employee, neither customer nor ticket is needed. + * Free to enter/leave + */ + if ($card->type == Card::TYPE_EMPLOYEE) { + $doorLog->save(false); + \Yii::$app->response->statusCode = 204; + \Yii::error("employee card {$card->id_card} event finished in sec " . $stopWatch->split()); + \Yii::$app->db->transaction->commit(); + return; + } + + //otherwise ticket is required + $activeTickets = Ticket::readActive($card, clone $date); + \Yii::error('active ticket count:' . count($activeTickets)); + /** @var Ticket $ticket */ + $ticket = null; + if (isset($activeTickets) && count($activeTickets) > 0) { + $ticket = $activeTickets[0]; + } + $doorLog->id_ticket_current = $ticket->id_ticket; + + + if (!isset($ticket)) { + throw new BadRequestHttpException("No active ticket found for:" . $card->number); + } + \Yii::error("ticket {$ticket->id_ticket} loaded in sec " . $stopWatch->split()); + + // customer is also required + $customer = $card->customer; + if (!isset($customer)) { + throw new BadRequestHttpException("Customer not found for:" . $card->number); + } + + $doorLog->id_customer = $customer->id_customer; + \Yii::error("customer {$customer->id_customer} loaded in sec " . $stopWatch->split()); + + // save the door log + $doorLog->save(false); + \Yii::error("door log {$doorLog->id_door_log} saved in sec " . $stopWatch->split()); + + // if direction is in + if ($direction == DoorLog::$DIRECTION_IN || $direction == DoorLog::$DIRECTION_IN_WITHOUT_MOVE) { + + if ($card->isFlagDoor()) { + throw new BadRequestHttpException("Card already 'IN': " . $card->id_card); + } + + if ($card->isFlagKey()) { + throw new BadRequestHttpException("Key required: " . $card->id_card); + } + + if ($card->isFlagStatus()) { + throw new BadRequestHttpException("Card has no active status: " . $card->id_card); + } + + $countDoorLogsForTicketSince = $this->getCountDoorLogsForTicketSince($ticket->id_ticket, DateUtil::utcDate(clone $date)); + \Yii::error("count of door logs '{$countDoorLogsForTicketSince}' loaded in sec " . $stopWatch->split()); + + // if the current event is the first door log today + if ($countDoorLogsForTicketSince == 1) { + // increase the ticket usage count with 1 + $usageCount = $ticket->usage_count; + $ticket->usage_count += 1; + $ticket->save(false); + Log::log( + [ + 'type' => Log::$TYPE_TICKET_USAGE_FIRST, + 'message' => 'Bérlet használat(előtte: ' . $usageCount . ' -> utána: ' . $ticket->usage_count, + 'id_ticket' => $ticket->id_ticket, + 'id_door_log' => $doorLog->id_door_log + ] + ); + \Yii::error("Ticket usage count increased after first doorlog of day in sec " . $stopWatch->split()); + + } else { + + // we have already a door log for today, other than this + // Now we split the day into 3hour intervalls, starting with the createdAt value of the first event. + // If the actual event happens in an interval, in which still now doorlog event happend, we increase + // the usage count with 1 + // 3 óránként 1-et levonunk + + $startOfDay = DateUtil::utcDate(clone $date); + $startOfTomorrow = DateUtil::tomorrowStart(clone $date); + + $allDoorLogToday = DoorLog::find() + ->andWhere(['>=', 'door_log.created_at', DateUtil::formatDateUtc($startOfDay)]) + ->andWhere(['<', 'door_log.created_at', DateUtil::formatDateUtc($startOfTomorrow)]) + ->andWhere(['id_ticket_current' => $ticket->id_ticket]) + ->andWhere(['in', 'direction', [DoorLog::$DIRECTION_IN_WITHOUT_MOVE, DoorLog::$DIRECTION_IN]]) + ->orderBy(['door_log.created_at' => SORT_ASC]) + ->all(); + \Yii::error("All door logs for today loaded in sec " . $stopWatch->split()); + + $firstInToday = $allDoorLogToday[0]; + + if (isset($firstInToday)) { + + $firstEntryDateTimeToday = DateUtil::parseDateTime($firstInToday->created_at); + + $interval = \DateInterval::createFromDateString('3 hours'); + $daterange = new \DatePeriod($firstEntryDateTimeToday, $interval, $startOfTomorrow); + + $intervals = []; + $intervalStart = null; + foreach ($daterange as $intervalEnd) { + if (isset($intervalStart)) { + $intervals[] = $this->createTicketUsageInterval($intervalStart, $intervalEnd, $allDoorLogToday, $doorLog); + } + $intervalStart = clone $intervalEnd; + } + + if ($intervalStart < $startOfTomorrow) { + $intervals[] = $this->createTicketUsageInterval($intervalStart, $startOfTomorrow, $allDoorLogToday, $doorLog); + } + + $activeInterval = $this->getActiveInterval($intervals, $createdAt); + if (!isset($activeInterval)) { + throw new ServerErrorHttpException("Active Interval not found"); + } + + $logCountInActiveInterval = count($activeInterval['logs']); + + if ($logCountInActiveInterval == 1) { + $ticket->usage_count = $ticket->usage_count + 1; + $ticket->save(false); + \Yii::error("Ticket usage count increased after first IN after first door_log in interval in sec " . $stopWatch->split()); + + } + } + + } + } + + if ($direction == DoorLog::$DIRECTION_OUT || $direction == DoorLog::$DIRECTION_OUT_WITHOUT_MOVE) { + + if ($card->isFlagOutKey()) { + throw new BadRequestHttpException("Can't exit with card has a key assigned"); + } + + if ($card->isFlagStatus()) { + throw new BadRequestHttpException("Can't exit with card has inactive status"); + } + + $ticket->count_move_out = $ticket->usage_count; + $ticket->save(false); + \Yii::error("direction_out: ticket count_move_out set after direction_out in sec " . $stopWatch->split()); + } + + Card::updateCardFlagTicket($ticket->id_ticket); + \Yii::error("updateCardFlagTicket: card flag updated in sec " . $stopWatch->split()); + + // reload card after flag is set + $card = Card::readCard($cardNumber); + if ($card->type != Card::TYPE_EMPLOYEE) { + if ($direction == DoorLog::$DIRECTION_OUT_WITHOUT_MOVE || $direction == DoorLog::$DIRECTION_OUT) { + $card->flag_out = Helper::setBit($card->flag_out, Card::$FLAG_DOOR, true); + $card->flag = Helper::setBit($card->flag, Card::$FLAG_DOOR, false); + $card->save(false); + \Yii::error("direction_out: Door flag updated in sec " . $stopWatch->split()); + + } else if ($direction == DoorLog::$DIRECTION_IN || $direction == DoorLog::$DIRECTION_IN_WITHOUT_MOVE) { + $card->flag_out = Helper::setBit($card->flag_out, Card::$FLAG_DOOR, false); + $card->flag = Helper::setBit($card->flag, Card::$FLAG_DOOR, true); + $card->save(false); + \Yii::error("direction_in: Card flag updated in sec " . $stopWatch->split()); + } + } + + + $stopWatch->stop(); + \Yii::error("finished in sec " . $stopWatch->getTotal()); + \Yii::$app->db->transaction->commit(); + } catch (Exception $e) { + \Yii::$app->db->transaction->rollBack(); + throw $e; } - - } - function getActiveInterval($intervals,$date){ - foreach ($intervals as $interval ){ + function getActiveInterval($intervals, $date) + { + foreach ($intervals as $interval) { $start = $interval['start']; $end = $interval['end']; - if ( $start <= $date && $date < $end ){ + if ($start <= $date && $date < $end) { return $interval; } } return null; } - function createTicketUsageInterval($start,$end, $allLogs, $actualDoorLog){ - $result = ['start' => $start, 'end' => $end , 'logs' =>[] ]; - foreach ($allLogs as $log){ + + function createTicketUsageInterval($start, $end, $allLogs, $actualDoorLog) + { + $result = ['start' => $start, 'end' => $end, 'logs' => []]; + foreach ($allLogs as $log) { $createdAt = DateUtil::parseDateTime($log->created_at); - if ( $createdAt >= $start && $createdAt < $end){ + if ($createdAt >= $start && $createdAt < $end) { $result['logs'][] = $log; } } @@ -285,9 +317,15 @@ class DoorManager extends BaseObject public function resetLogs($cardNumber) { $card = Card::readCard($cardNumber); + $card->flag = 0; + $card->flag_out = 0; + $card->save(false); + Card::updateCardFlagTicket($card->id_card); DoorLog::deleteAll( ['id_card' => $card->id_card] ); + // todo: revoke all assigned key + $this->revokeKey($cardNumber,"f100"); } public function getLogs($cardNumber) @@ -330,16 +368,16 @@ class DoorManager extends BaseObject public function createLog() { - \Yii::error("Post create log:". \Yii::$app->request->method); + \Yii::error("Post create log:" . \Yii::$app->request->method); if (\Yii::$app->request->isPost) { $log = new DoorLogForTest(); if ($log->load(\Yii::$app->request->post(), "")) { if ($log->validate()) { - \Yii::error("Door log saving:".$log->created_at); + \Yii::error("Door log saving:" . $log->created_at); $log->save(false); return $log; - }else{ - throw new BadRequestHttpException(print_r($log->getErrors(),true)); + } else { + throw new BadRequestHttpException(print_r($log->getErrors(), true)); } } else { \Yii::error("validated" . print_r($log->errors, true)); @@ -349,4 +387,24 @@ class DoorManager extends BaseObject throw new BadRequestHttpException('Not a Post'); } + function checkoutKey($cardNumber, $keyNumber) + { + $model = new KeyToggleForm(); + $model->card = Card::readCard($cardNumber); + $model->customer = $model->card->customer; + $model->keyCard = $model->card; + $model->keyModel = $model->readKey($keyNumber); + $model->assign(); + } + + function revokeKey($cardNumber, $keyNumber) + { + $model = new KeyToggleForm(); + $model->card = Card::readCard($cardNumber); + $model->customer = $model->card->customer; + $model->keyCard = $model->card; + $model->keyModel = $model->readKey($keyNumber); + $model->revoke(); + } + } diff --git a/common/models/DoorLog.php b/common/models/DoorLog.php index 1e6c44d..7feb93f 100644 --- a/common/models/DoorLog.php +++ b/common/models/DoorLog.php @@ -35,7 +35,7 @@ class DoorLog extends \yii\db\ActiveRecord public static $DIRECTION_ALL_MANUAL_READ = 0; // "Kézi olvasás", public static $DIRECTION_OUT_WITHOUT_MOVE = 1; // "KI olvastatás mozgás nélkül", public static $DIRECTION_IN_WITHOUT_MOVE = 3; // "BE olvastatás mozgás nélkül", - public static $DIRECTION_OUT_ = 5; // "KI mozgás", + public static $DIRECTION_OUT = 5; // "KI mozgás", public static $DIRECTION_IN = 7; // "BE mozgás", public static $DIRECTION_OUT_ERROR_KEY_ASSIGNED = 9; // "KI olvastatás, van érvényes öltöző kulcs (nem enged)", public static $DIRECTION_IN_ERROR_KEY_MISSING = 11; // "BE olvastatás, nincs érvényes öltöző kulcs (nem enged)", diff --git a/doc/trigger.sql b/doc/trigger.sql index 4ab1022..22eabd2 100644 --- a/doc/trigger.sql +++ b/doc/trigger.sql @@ -103,7 +103,7 @@ begin IF NEW.direction = 5 or New.direction = 1 then - +/* select max(ticket.id_ticket) into @p_mo_ticket_id from ticket where ticket.start <= CURDATE() @@ -113,13 +113,13 @@ begin and ticket.id_card = New.id_card group by id_card order by id_card desc; +*/ - - set @p_allow_enter = true; +# set @p_allow_enter = true; update card set flag_out = ( flag_out | 1 << 1 ) , - flag = case when @p_allow_enter then ( flag & ~(1 << 1) ) else ( flag | 1 << 1 ) end + flag = ( flag & ~(1 << 1) ) WHERE type <> 50 and id_card = New.id_card; END IF; diff --git a/frontend/models/KeyToggleForm.php b/frontend/models/KeyToggleForm.php index c9c2a24..7255376 100644 --- a/frontend/models/KeyToggleForm.php +++ b/frontend/models/KeyToggleForm.php @@ -31,8 +31,8 @@ class KeyToggleForm extends Model public $action; public $keyCard; - - + + /** * @inheritdoc */ @@ -52,20 +52,24 @@ class KeyToggleForm extends Model ]; } + public function readKey($keyNumber){ + $query= Key::find(); + $fixedKeyNumber = Helper::fixAsciiChars($keyNumber); + // add condition rfid key or number + $query->andWhere(['or', + ['and',[ 'in','key.number' , [$fixedKeyNumber]],"trim(coalesce(key.number, '')) <>'' " ], + ['and', ['in','key.rfid_key' ,[ $fixedKeyNumber ] ],"trim(coalesce(key.rfid_key, '')) <>'' "], + + ]); + return $query->one(); + } + /** * */ public function toggleKey(){ $query= Key::find(); - $this->key = Helper::fixAsciiChars($this->key); - // add condition rfid key or number - $query->andWhere(['or', - ['and',[ 'in','key.number' , [$this->key]],"trim(coalesce(key.number, '')) <>'' " ], - ['and', ['in','key.rfid_key' ,[ $this->key ] ],"trim(coalesce(key.rfid_key, '')) <>'' "], - - ]); - - $this->keyModel = $query->one(); + $this->keyModel = $this->readKey($this->key); //ha van ilyen kulcs if ( isset($this->keyModel) ){ //find card - key assignments @@ -74,27 +78,7 @@ class KeyToggleForm extends Model $assignments = CardKeyAssignment::find()->andWhere(['id_key' => $this->keyModel->id_key])->all(); //if assignment found - we will unassign it if ( count($assignments) > 0){ - if ( isset( $this->keyCard ) ){ - - CardKeyAssignment::deleteAll(['id_key' => $this->keyModel->id_key]); - $this->keyCard->setFlagsHasKey(false); - $this->keyCard->save(false); - /** @noinspection PhpUndefinedClassInspection */ - \Yii::$app->session->setFlash ( 'success', 'Kulcs visszaadva!' ); - $this->action = 'unassign'; - Log::log([ - 'type' =>Log::$TYPE_KEY_ASSIGN, - 'message' => 'Kulcs visszaadás - Kártya/Kulcs/Vendég:' .$this->keyCard->number ."/" .$this->keyModel->number . "/".$this->keyCard->customer->name, - 'id_key' => $this->keyModel->id_key, - 'id_customer' => $this->keyCard->customer->id_customer - ]); - DoorLog::mkDoorLog(-1,$this->keyCard,$this->keyCard->customer,$this->keyModel ); - }else{ - /** @noinspection PhpUndefinedClassInspection */ - \Yii::error("Key for assignment not found"); - /** @noinspection PhpUndefinedClassInspection */ - \Yii::$app->session->setFlash ( 'danger', 'Kulcs visszaadás hiba: kulcs nem található!' ); - } + $this->revoke(); }else{ // if there is no assignment - assign it to the customer $this->assign(); @@ -105,6 +89,27 @@ class KeyToggleForm extends Model } } + public function revoke(){ + if ( isset( $this->keyCard ) ){ + + CardKeyAssignment::deleteAll(['id_key' => $this->keyModel->id_key]); + $this->keyCard->setFlagsHasKey(false); + $this->keyCard->save(false); + \Yii::$app->session->setFlash ( 'success', 'Kulcs visszaadva!' ); + $this->action = 'unassign'; + Log::log([ + 'type' =>Log::$TYPE_KEY_ASSIGN, + 'message' => 'Kulcs visszaadás - Kártya/Kulcs/Vendég:' .$this->keyCard->number ."/" .$this->keyModel->number . "/".$this->keyCard->customer->name, + 'id_key' => $this->keyModel->id_key, + 'id_customer' => $this->keyCard->customer->id_customer + ]); + DoorLog::mkDoorLog(-1,$this->keyCard,$this->keyCard->customer,$this->keyModel ); + }else{ + \Yii::error("Key for assignment not found"); + \Yii::$app->session->setFlash ( 'danger', 'Kulcs visszaadás hiba: kulcs nem található!' ); + } + } + public function assign(){ if ( isset($this->card) && isset($this->customer) ){ $assignments = CardKeyAssignment::find()->andWhere(['id_card' => $this->card->id_card])->all(); @@ -139,6 +144,6 @@ class KeyToggleForm extends Model \Yii::$app->session->setFlash ( 'danger', 'Nincs vendég kiválasztva vagy érvénytelen kártya!' ); } } - + } diff --git a/rest/config/main.php b/rest/config/main.php index 8dbc9eb..4b48aad 100644 --- a/rest/config/main.php +++ b/rest/config/main.php @@ -32,7 +32,7 @@ return [ [ 'class' => 'yii\log\FileTarget', 'levels' => ['error', 'warning'], - ], + ] ], ], 'errorHandler' => [ diff --git a/rest/controllers/DoorController.php b/rest/controllers/DoorController.php index 184bbd3..29907bd 100644 --- a/rest/controllers/DoorController.php +++ b/rest/controllers/DoorController.php @@ -45,5 +45,14 @@ class DoorController extends RestController echo "hello"; } + public function actionCheckoutKey($cardNumber, $keyNumber){ + $doorManager = new DoorManager(); + $doorManager->checkoutKey($cardNumber,$keyNumber); + } + + public function actionRevokeKey($cardNumber, $keyNumber){ + $doorManager = new DoorManager(); + $doorManager->revokeKey($cardNumber,$keyNumber); + } } diff --git a/test/src/rest/move.test.js b/test/src/rest/move.test.js index 6953d58..002c1da 100644 --- a/test/src/rest/move.test.js +++ b/test/src/rest/move.test.js @@ -5,6 +5,7 @@ const axios = require('axios'); const url = 'http://localhost:86/fitness_web/rest/web/index.php?r='; const auth = "Basic ZG9vcl9zeXN0ZW06ZG9vcnN5c3RlbTE="; const TEST_CARD_NUMBER = "10WMVXMZ"; +const TEST_KEY = "f100"; const TEST_AUTH_HEADER = { 'Authorization': auth }; @@ -29,7 +30,6 @@ function formatDateTime(date){ + (date.getMinutes()+"").padStart(2,"0") + ":" + "00"; - console.info(s); return s; } @@ -74,7 +74,24 @@ class Client { }); } + async checkoutKey(cardNumber,keyNumber) { + return await axios.get(url + `door/checkout-key&cardNumber=${cardNumber}&keyNumber=${keyNumber}`, { + headers: { + ...TEST_AUTH_HEADER, + } + }); + } + + async revokeKey(cardNumber,keyNumber) { + return await axios.get(url + `door/revoke-key&cardNumber=${cardNumber}&keyNumber=${keyNumber}`, { + headers: { + ...TEST_AUTH_HEADER, + } + }); + } + async moveWithInfo(data, headers) { + await this.checkoutKey(TEST_CARD_NUMBER,TEST_KEY); const before = await this.getInfo(data.cardNumber); const response = await this.move(data, headers); const after = await this.getInfo(data.cardNumber); @@ -94,7 +111,6 @@ test('Emergency open', async () => { let response = await client.move({direction: 128}) expect(response.status).toBe(204); let after = await client.getInfo(TEST_CARD_NUMBER); - // console.info("before:after", before.data.doorLogCount, after.data.doorLogCount); expect(after.data.doorLogs.length).toBe(0); expect(after.data.doorLogCount).toBe("" + (parseInt(before.data.doorLogCount) + 1)); expect(after.data.lastDoorLog?.direction).toBe(128); @@ -113,38 +129,50 @@ test('Error if card not exists', async () => { } expect.assertions(1); -}) +},10000); test('Allow card type employee', async () => { const client = new Client(); let createdAt = new Date(); createdAt.setHours(18); - const info = await client.moveWithInfo({cardNumber: 'employee', direction: 3, createdAt: secondsSinceEpoch(createdAt)}); - // console.info("employee ready"); + const info = await client.moveWithInfo({cardNumber: 'employee', direction: 3, createdAt: formatDateTime(createdAt)}); const lastLog = info.after.data.lastDoorLog; expect(lastLog.id_card).not.toBeNull(); expect(lastLog.direction).toBe(3); -}) +},10000) test('Normal Ticket: usage count will be increased on entry', async () => { const client = new Client(); - client.reset(TEST_CARD_NUMBER); + await client.reset(TEST_CARD_NUMBER); const info = await client.moveWithInfo({cardNumber: TEST_CARD_NUMBER, direction: 3}); expect(info?.response?.status).toBe(200); const ticketBefore = info?.before.data?.tickets[0]; const ticketAfter = info?.after.data?.tickets[0]; expect(ticketBefore.usage_count + 1).toBe(ticketAfter.usage_count); + await client.revokeKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({cardNumber: TEST_CARD_NUMBER, direction: 5}); + const info2 = await client.moveWithInfo({cardNumber: TEST_CARD_NUMBER, direction: 3}); expect(info?.response?.status).toBe(200); const ticketAfter2 = info2?.after.data?.tickets[0]; expect(ticketAfter.usage_count).toBe(ticketAfter2.usage_count); +},20000); + +test('Normal Ticket: flag door in will be set on entry', async () => { + const client = new Client(); + await client.reset(TEST_CARD_NUMBER); + const info = await client.moveWithInfo({cardNumber: TEST_CARD_NUMBER, direction: 3}); + expect(info?.response?.status).toBe(200); + expect(info.before.data.card.flag).toBe(0); + expect(info.after.data.card.flag).toBe(2); }) + test('Normal Ticket: increase door log after 3 hours', async () => { const client = new Client(); - client.reset(TEST_CARD_NUMBER); + await client.reset(TEST_CARD_NUMBER); let info = await client.getInfo(TEST_CARD_NUMBER); // First log on this day let date = new Date(); @@ -170,6 +198,12 @@ test('Normal Ticket: increase door log after 3 hours', async () => { let ticketBefore = info?.before.data?.tickets[0]; let ticketAfter = info?.after.data?.tickets[0]; expect(ticketBefore.usage_count).toBe(ticketAfter.usage_count); + await client.revokeKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 5, + createdAt: formatDateTime(createdAt) + }); // @4:00 3 hours already passed, we need consume usage count createdAt = new Date(); @@ -183,9 +217,16 @@ test('Normal Ticket: increase door log after 3 hours', async () => { ticketBefore = info?.before.data?.tickets[0]; ticketAfter = info?.after.data?.tickets[0]; expect(ticketBefore.usage_count+1).toBe(ticketAfter.usage_count); + await client.revokeKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 5, + createdAt: formatDateTime(createdAt) + }); // @6:00 2 hours passed since 4, we don't need consume usage count createdAt = new Date(); + await client.revokeKey(TEST_CARD_NUMBER,TEST_KEY); createdAt.setHours(5); createdAt.setMinutes(0,0,0); info = await client.moveWithInfo({ @@ -196,6 +237,12 @@ test('Normal Ticket: increase door log after 3 hours', async () => { ticketBefore = info?.before.data?.tickets[0]; ticketAfter = info?.after.data?.tickets[0]; expect(ticketBefore.usage_count).toBe(ticketAfter.usage_count); + await client.revokeKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 5, + createdAt: formatDateTime(createdAt) + }); // @20:15 Need to consume usage count createdAt = new Date(); @@ -209,4 +256,94 @@ test('Normal Ticket: increase door log after 3 hours', async () => { ticketBefore = info?.before.data?.tickets[0]; ticketAfter = info?.after.data?.tickets[0]; expect(ticketBefore.usage_count+1).toBe(ticketAfter.usage_count); -},10000) +},20000) + +test('Normal Ticket: can\'t move 2 times in without move out', async () => { + + const client = new Client(); + await client.reset(TEST_CARD_NUMBER); + await client.checkoutKey(TEST_CARD_NUMBER,TEST_KEY); + + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 3 + }); + try { + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 3 + }); + // prev statement must fail + expect(true).toBe(false); + }catch (e){ + expect(e).toBeDefined(); + } + +},10000); + +test('Normal Ticket: can\'t move 2 times \'OUT\' without move in', async () => { + + const client = new Client(); + await client.reset(TEST_CARD_NUMBER); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 5 + }); + try { + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 5 + }); + // prev statement must fail + expect(true).toBe(false); + }catch (e){ + expect(e).toBeDefined(); + } + +},10000); + +test('Normal Ticket: move in, move out, move in, move out', async () => { + + const client = new Client(); + await client.reset(TEST_CARD_NUMBER); + await client.checkoutKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 3 + }); + await client.revokeKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 5 + }); + await client.checkoutKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 3 + }); + await client.revokeKey(TEST_CARD_NUMBER,TEST_KEY); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 5 + }); + +},10000); + + +test('Normal Ticket: can\'t move \'in\' without key', async () => { + + try{ + const client = new Client(); + await client.reset(TEST_CARD_NUMBER); + await client.move({ + cardNumber: TEST_CARD_NUMBER, + direction: 3 + }); + // should throw an exception, since no key is assigend + expect(true).toBe(false); + }catch (e){ + expect(e).toBeDefined(); + } + + +},10000);