diff --git a/common/components/DateUtil.php b/common/components/DateUtil.php index 55132d1..e8d9b05 100644 --- a/common/components/DateUtil.php +++ b/common/components/DateUtil.php @@ -159,7 +159,6 @@ class DateUtil } public static function parseDate($dateString){ - $date = DateTime::createFromFormat('Y.m.d', $dateString, new DateTimeZone( 'UTC')); $date->setTime(0, 0); return $date; @@ -187,4 +186,22 @@ class DateUtil return $translations[$weekDay]; } + /** + * @param $dateTime \DateTime + * @return void + */ + public static function resetSeconds($dateTime){ + return $dateTime->setTime($dateTime->format("H"), $dateTime->format("i"),0); + } + + /** + * @param $dateTime \DateTime + * @param $minutes integer + * @return mixed + * @throws Exception + */ + public static function addMinutes($dateTime, $minutes){ + return $dateTime->add(new \DateInterval('PT' . $minutes . 'M')); + } + } diff --git a/common/components/DoorMoveContext.php b/common/components/DoorMoveContext.php new file mode 100644 index 0000000..fe5574b --- /dev/null +++ b/common/components/DoorMoveContext.php @@ -0,0 +1,33 @@ +message = $message; + $this->type = $type; + $this->errorCode = $errorCode; + $this->payload = $payload; + $this->originalException = $originalException; + } + + + +} diff --git a/common/components/Helper.php b/common/components/Helper.php index 81d8ff6..72c6148 100644 --- a/common/components/Helper.php +++ b/common/components/Helper.php @@ -548,4 +548,8 @@ class Helper { return Helper::getArrayValue(\Yii::$app->params ,'door_entry_strategy','strategy_key'); } + public static function getDoorPassValidityIntervalMinutes(){ + return Helper::getArrayValue(\Yii::$app->params ,'door_pass_validity_interval_minutes','10'); + } + } diff --git a/common/manager/DoorCardPassManager.php b/common/manager/DoorCardPassManager.php new file mode 100644 index 0000000..fb27379 --- /dev/null +++ b/common/manager/DoorCardPassManager.php @@ -0,0 +1,143 @@ +id_card = $idCard; + + $validUntilTime = DateUtil::utcDateTime(); + $validUntilTime->add(new DateInterval('PT' . Helper::getDoorPassValidityIntervalMinutes() . 'M')); + $validUntilTime->setTime($validUntilTime->format("H"), $validUntilTime->format("i"), 0); + $model->valid_until_at = DateUtil::formatDateTimeUtc($validUntilTime); + $model->save(false); + return $model; + } + + public function hardReset(){ + $now = DateUtil::utcDateTime(); + DoorCardPass::updateAll( + [ + 'invalidated_at' => DateUtil::formatDateTimeUtc($now) + ], + [ + 'invalidated_at' => null + ] + ); + \Yii::$app->db->createCommand(Card::SQL_DENY_DOOR_CARD_PASS())->execute(); + } + + /** + * @throws NotFoundHttpException + */ + public function updateDoorCardPassStateForCard($idCard) + { + $now = DateUtil::utcDateTime(); + $card = Card::findOne($idCard); + if (!isset($card)) { + throw new NotFoundHttpException(); + } + $passes = $this->readAllNovInvalidatedAtCardPassForCard($idCard,$now); + $this->setCardFlagAndInvalidateDoorCardPasses($card, $passes,$now); + } + + public function updateDoorCardPassStateForAllCard(){ + $passes = $this->readAllNonInvalidated(); + $map = array(); + foreach ($passes as $pass){ + $idCard = $pass->id_card; + if ( !array_key_exists($idCard,$map)){ + $map[$idCard] = array(); + } + $map[$idCard][] = $pass; + } + + $now = DateUtil::utcDateTime(); + + foreach ($map as $idCard => $passes){ + $card = Card::findOne($idCard); + if (isset($card)) { + $this->setCardFlagAndInvalidateDoorCardPasses($card, $passes,$now); + } + } + } + + private function readAllNovInvalidatedAtCardPassForCard($idCard) + { + return DoorCardPass::find() + ->andWhere( + [ + 'id_card' => $idCard, + 'invalidated_at' => null, + ] + )->all(); + } + + private function readAllNonInvalidated() + { + return DoorCardPass::find() + ->andWhere( + ['invalidated_at' => null] + )->all() ; + } + + private function setCardFlagAndInvalidateDoorCardPasses($card, $doorCardPasses, $now) + { + $allowed = $this->hasActiveDoorCardPass($doorCardPasses, $now); + $origFlag = $card->flag; + $card->flag = Helper::setBit($card->flag, Card::$FLAG_DOOR_PASS_ALLOWED, !$allowed); + if ($card->flag !== $origFlag) { + $card->save(false); + } + $this->invalidateNonActiveDoorCardPasses($doorCardPasses, $now); + } + + private function invalidateNonActiveDoorCardPasses($doorCardPasses, $now) + { + foreach ($doorCardPasses as $pass) { + if (isset($pass->valid_until_at) && !empty($pass->valid_until_at)) { + \Yii::error( "mydate". print_r($pass->valid_until_at ,true)); + + $validUntilAt = DateUtil::parseDateTime($pass->valid_until_at); + $active = ($validUntilAt >= $now); + if (!$active) { + $pass->invalidated_at = DateUtil::formatDateTimeUtc($now); + $pass->save(false); + } + } + } + } + + private function hasActiveDoorCardPass($doorCardPasses, $now) + { + $active = false; + foreach ($doorCardPasses as $pass) { + if (isset($pass->valid_until_at)) { + $validUntilAt = DateUtil::parseDateTime($pass->valid_until_at); + $active |= ($validUntilAt >= $now); + } + } + return $active; + } + + +} diff --git a/common/manager/DoorManager.php b/common/manager/DoorManager.php index 968208e..6673ec6 100644 --- a/common/manager/DoorManager.php +++ b/common/manager/DoorManager.php @@ -3,6 +3,8 @@ namespace common\manager; use common\components\DateUtil; +use common\components\DoorMoveContext; +use common\components\FitnessException; use common\components\Helper; use common\components\StopWatch; use common\models\Card; @@ -37,108 +39,185 @@ class DoorManager extends BaseObject * @param $createdAt number unix timestamp , override createdAt for doorLogs * @param $date number unix timestamp, override date for validation check 'now' * @return void - * @throws BadRequestHttpException - * @throws ServerErrorHttpException - * @throws \yii\base\InvalidConfigException - * @throws \yii\db\Exception */ public function move($identifier, $device, $direction, $verifyOnly, $createdAt = null, $date = null) { - $requestId = uniqid("",false); - $stopWatch = new StopWatch(); + $requestId = uniqid("", false); + $context = new DoorMoveContext([ + 'id' => $requestId, + '$identifier' => $identifier, + 'device' => $device, + 'direction' => $direction, + 'originalDirection' => $direction, + 'verifyOnly' => $verifyOnly + ]); + try { + $stopWatch = new StopWatch(); - // for testing purposes - if ( Helper::isRestAllowVerifyOnly() === false ){ - \Yii::info("$requestId: verifyonly not allowed"); - $verifyOnly = false; - } - - \Yii::info("$requestId: move with next parameers:" . ";identifier" . $identifier . ";device" . $device . ";direction" . $direction . ";verifyOnly" . $verifyOnly . ";createdAt" . print_r($createdAt,true) . ";date" . print_r($date,true)); - \Yii::info("$requestId: move get request: " . print_r($_GET, true)); - \Yii::info("$requestId: move post request: " . print_r($_GET, true)); - - if (isset($createdAt)) { - $createdAt = DateUtil::parseDateTime($createdAt); - } else { - $createdAt = DateUtil::utcDateTime(); - } - - if (isset($date)) { - $date = DateUtil::parseDateTime($date); - } else { - $date = DateUtil::utcDate(); - } - - if ($device === 'E') { - $this->moveEmergency($requestId,$identifier, $device, $direction, $verifyOnly, $createdAt, $date); - return; - } - - $cardNumber = $identifier; - $virtualKey = null; - - switch ($direction) { - case 'IN': - $direction = DoorLog::$DIRECTION_IN; - break; - case 'OUT': - $direction = DoorLog::$DIRECTION_OUT; - break; - default: - throw new BadRequestHttpException("$requestId: Direction not supported: " . $direction); - } - - // if device is qr code - if ($device == 'Q') { - // allow only virtual key - $virtualKey = VirtualKey::findOne(['number' => $identifier]); - if (!isset($virtualKey)) { - throw new BadRequestHttpException("$requestId: Virtual key not found: " . $identifier); + // for testing purposes + if (Helper::isRestAllowVerifyOnly() === false) { + \Yii::info("$requestId: verifyonly not allowed"); + $verifyOnly = false; } - $card = Card::findOne($virtualKey->id_card); - if ($card != null) { - $card = Card::readCard($card->number); - } - if ($card == null) { - throw new BadRequestHttpException("$requestId: Card not found by virtual key: " . $identifier . '/' . $virtualKey->id_card); - } - $cardNumber = $card->number; - \Yii::info("$requestId: virtual key and card loaded in sec " . $stopWatch->split()); - } else { - // load by rfid or card number - $card = Card::readCard(Helper::fixAsciiChars($identifier)); - \Yii::info("$requestId: Card loaded in sec " . $stopWatch->split()); - if (!isset($card)) { - throw new BadRequestHttpException("$requestId: Card not found with number: " . $identifier); - } - \Yii::info("$requestId: card loaded in sec " . $stopWatch->split()); - $virtualKey = VirtualKey::findOne(['id_card' => $card->id_card]); - \Yii::info("$requestId: virtual key for card loaded in sec " . $stopWatch->split()); + \Yii::info("$requestId: move with next parameers:" . ";identifier" . $identifier . ";device" . $device . ";direction" . $direction . ";verifyOnly" . $verifyOnly . ";createdAt" . print_r($createdAt, true) . ";date" . print_r($date, true)); + \Yii::info("$requestId: move get request: " . print_r($_GET, true)); + \Yii::info("$requestId: move post request: " . print_r($_GET, true)); + + if (isset($createdAt)) { + $createdAt = DateUtil::parseDateTime($createdAt); + } else { + $createdAt = DateUtil::utcDateTime(); + } + + $context->created_at = $createdAt; + + if (isset($date)) { + $date = DateUtil::parseDateTime($date); + } else { + $date = DateUtil::utcDate(); + } + + $context->date = $date; + + if ($device === 'E') { + $this->moveEmergency($context/*;$requestId,$identifier, $device, $direction, $verifyOnly, $createdAt, $date*/); + return; + } + + + $cardNumber = $identifier; + $virtualKey = null; + + switch ($direction) { + case 'IN': + $direction = DoorLog::$DIRECTION_IN; + break; + case 'OUT': + $direction = DoorLog::$DIRECTION_OUT; + break; + default: + throw new BadRequestHttpException("$requestId: Direction not supported: " . $direction); + } + $context->direction = $direction; + + // if device is qr code + if ($device == 'Q') { + // allow only virtual key + $virtualKey = VirtualKey::findOne(['number' => $identifier]); + + if (!isset($virtualKey)) { + $context->error = true; + $context->errorCode = "VIRTUAL_KEY_NOT_FOUND"; + throw new BadRequestHttpException("$requestId: Virtual key not found: " . $identifier); + } + $card = Card::findOne($virtualKey->id_card); + if ($card != null) { + $card = Card::readCard($card->number); + } + if ($card == null) { + $context->error = true; + $context->errorCode = "CARD_NOT_FOUND"; + throw new BadRequestHttpException("$requestId: Card not found by virtual key: " . $identifier . '/' . $virtualKey->id_card); + } + $cardNumber = $card->number; + \Yii::info("$requestId: virtual key and card loaded in sec " . $stopWatch->split()); + } else { + // load by rfid or card number + $card = Card::readCard(Helper::fixAsciiChars($identifier)); + \Yii::info("$requestId: Card loaded in sec " . $stopWatch->split()); + if (!isset($card)) { + $context->error = true; + $context->errorCode = "CARD_NOT_FOUND"; + throw new BadRequestHttpException("$requestId: Card not found with number: " . $identifier); + } + \Yii::info("$requestId: card loaded in sec " . $stopWatch->split()); + $virtualKey = VirtualKey::findOne(['id_card' => $card->id_card]); + \Yii::info("$requestId: virtual key for card loaded in sec " . $stopWatch->split()); + } + + $context->virtualKey = $virtualKey; + $context->card = $card; + $context->cardNumber = $cardNumber; + + \Yii::info("$requestId: Card number " . $card->number); + + if ($card->type == Card::TYPE_EMPLOYEE) { + $this->moveEmployee($context /*$identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey*/); + return; + } + + $this->moveCustomer($context /* $requestId, $identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey*/); + + } catch (FitnessException $e) { + $context->error = true; + $context->exception = $e->originalException; + $context->errorCode = $e->errorCode; + } catch (\Exception $e) { + $context->error = true; + $context->exception = $e; + $context->errorCode = "UNKNOWN"; + } finally { + // do logging + $this->logContext($context); } - - \Yii::info("$requestId: Card number " . $card->number); - - if ($card->type == Card::TYPE_EMPLOYEE) { - $this->moveEmployee($identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey); - return; - } - - $this->moveCustomer($requestId, $identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey); - } - function moveEmergency($requestId,$identifier, $device, $direction, $verifyOnly, $createdAt, $date) + /** + * @param $ctx DoorMoveContext + * @return void + */ + function logContext($ctx){ + try{ + $result = [ + 'requestId' => $ctx->id, + 'identifier' => $ctx->identifier, + 'verifyOnly' => $ctx->verifyOnly, + 'device' => $ctx->device, + 'direction' => $ctx->direction, + 'originalDirection' => $ctx->originalDirection, + 'idCard' => isset($ctx->card)? $ctx->card->id_card : null, + 'cardNumber' => isset($ctx->card) ? $ctx->card->number : null, + 'idTicket' => isset($ctx->ticket) ? $ctx->ticket->id_ticket : null, + 'ticketType' => isset($ctx->ticket) ? $ctx->ticket->ticketType->name : null, + 'ticketUsageCount' => isset($ctx->ticket) ? $ctx->ticket->usage_count : null, + 'idCustomer' => isset($ctx->customer) ? $ctx->customer->id_customer : null, + 'customerName' => isset($ctx->customer) ? $ctx->customer->name : null, + 'customerEmail' => isset($ctx->customer) ? $ctx->customer->email : null, + 'key' => isset($ctx->key) ? $ctx->key->name : null, + 'idVirtualKey' => isset($ctx->virtualKey) ? $ctx->virtualKey->idVirtualKey : null, + 'kind' => $ctx->kind, + 'actions' => implode(",",$ctx->actions), + 'error' => $ctx->error, + 'errorCode' => $ctx->errorCode, + 'ticketUsage' => 1, + ]; + + if ( $ctx->error === true ){ + if ( isset($ctx->exception )){ + $result['errorMessage'] = substr( $ctx->exception->message,0,100); + } + } + + \Yii::info("door log: " . implode(";", $result)); + + }catch (\Exception $e){ + \Yii::error("Failed to log door context"); + } + } + + function moveEmergency($ctx) { - \Yii::info("$requestId: emergency move"); + $ctx->kind = "EMERGENCY"; + \Yii::info("$ctx->requestId: emergency move"); try { - $createdAtStr = DateUtil::formatDateTimeUtc($createdAt); + $createdAtStr = DateUtil::formatDateTimeUtc($ctx->createdAt); \Yii::$app->db->beginTransaction(); $doorLog = new DoorLog(); $doorLog->version = 2; $doorLog->direction = DoorLog::$DIRECTION_ALL_EMERGENCY; - $doorLog->source_app = $device; + $doorLog->source_app = $ctx->device; $doorLog->created_at = $createdAtStr; $doorLog->save(false); @@ -150,33 +229,42 @@ class DoorManager extends BaseObject ] ); + $ctx->actions[] = "EMERGENCY_MOVE"; + \Yii::$app->db->transaction->commit(); } catch (\Exception $e) { \Yii::$app->db->transaction->rollBack(); - throw $e; + throw new FitnessException( + "unknowon error", + FitnessException::UNKNOWN_ERROR, + "EMERGENCY_OPEN_FAILED", + $ctx, + $e + ); } } - function moveEmployee($identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey) + function moveEmployee($ctx /*$identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey*/) { + $ctx->kind = "EMPLOYEE"; /** * if the card type is employee, neither customer nor ticket is needed. * Free to enter/leave */ \Yii::info("employee move"); try { - $createdAtStr = DateUtil::formatDateTimeUtc($createdAt); + $createdAtStr = DateUtil::formatDateTimeUtc($ctx->createdAt); \Yii::$app->db->beginTransaction(); $doorLog = new DoorLog(); $doorLog->version = 2; - $doorLog->direction = $direction; - $doorLog->source_app = $device; + $doorLog->direction = $ctx->direction; + $doorLog->source_app = $ctx->device; $doorLog->created_at = $createdAtStr; - $doorLog->id_card = $card->id_card; - $doorLog->card_flag = $card->flag; + $doorLog->id_card = $ctx->card->id_card; + $doorLog->card_flag = $ctx->card->flag; - if (!$verifyOnly) { + if (!$ctx->verifyOnly) { $doorLog->save(false); Log::log( @@ -188,52 +276,53 @@ class DoorManager extends BaseObject ); } + $ctx->actions[] = "EMPLOYEE_" . $ctx->originalDirection; + \Yii::$app->db->transaction->commit(); } catch (\Exception $e) { \Yii::$app->db->transaction->rollBack(); - throw $e; + throw new FitnessException( + "unknowon error", + FitnessException::UNKNOWN_ERROR, + "EMPLOYEE_FAILED", + $ctx, + $e + ); } } /** - * @param $requestId - * @param $identifier string virtual key id, card rfid or card number - * @param $device string device - * @param $direction number direction - * @param $verifyOnly boolean only check or real move - * @param $createdAt - * @param $date - * @param $card - * @param $cardNumber - * @param $virtualKey + * @param $ctx + * * @return void * @throws BadRequestHttpException * @throws ServerErrorHttpException * @throws InvalidConfigException * @throws Exception */ - function moveCustomer($requestId, $identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey) + function moveCustomer($ctx/* $requestId, $identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey*/) { - \Yii::info("$requestId: move customer"); + $ctx->kind = "CUSTOMER"; + \Yii::info("$ctx->requestId: move customer"); $stopWatch = new StopWatch(); try { - $createdAtStr = DateUtil::formatDateTimeUtc($createdAt); - \Yii::info("$requestId: crated at str: ". $createdAtStr); + $createdAtStr = DateUtil::formatDateTimeUtc($ctx->createdAt); + \Yii::info("$ctx->requestId: crated at str: " . $createdAtStr); \Yii::$app->db->beginTransaction(); $doorLog = new DoorLog(); $doorLog->version = 2; - $doorLog->direction = $direction; - $doorLog->source_app = $device; + $doorLog->direction = $ctx->direction; + $doorLog->source_app = $ctx->device; $doorLog->created_at = $createdAtStr; - $doorLog->id_card = $card->id_card; - $doorLog->card_flag = $card->flag; + $doorLog->id_card = $ctx->card->id_card; + $doorLog->card_flag = $ctx->card->flag; - $activeTickets = Ticket::readActive($card, clone $date); - \Yii::info("$requestId: active ticket count:" . count($activeTickets)); + $activeTickets = Ticket::readActive($ctx->card, clone $ctx->date); + \Yii::info("$ctx->requestId: active ticket count:" . count($activeTickets)); /** @var Ticket $ticket */ $ticket = null; if (isset($activeTickets) && count($activeTickets) > 0) { @@ -241,69 +330,104 @@ class DoorManager extends BaseObject } if (!isset($ticket)) { - throw new BadRequestHttpException("$requestId: No active ticket found for:" . $card->number); + throw new FitnessException( + "$ctx->requestId: No active ticket found for:" . $ctx->card->number, + FitnessException::TYPE_BAD_REQUEST, + "NOT_FOUND_ACTIVE_TICKET", + $ctx + ); } + $ctx->ticket = $ticket; - \Yii::info("$requestId: ticket {$ticket->id_ticket} loaded in sec " . $stopWatch->split()); + \Yii::info("$ctx->requestId: ticket {$ticket->id_ticket} loaded in sec " . $stopWatch->split()); $doorLog->id_ticket_current = $ticket->id_ticket; // customer is also required - $customer = $card->customer; + $customer = $ctx->card->customer; if (!isset($customer)) { - throw new BadRequestHttpException("$requestId: Customer not found for:" . $card->number); + throw new FitnessException( + "$ctx->requestId: Customer not found for:" . $ctx->card->number, + FitnessException::TYPE_BAD_REQUEST, + "NOT_FOUND_CUSTOMER", + $ctx + ); } - $doorLog->id_customer = $customer->id_customer; - \Yii::info("$requestId: customer {$customer->id_customer} loaded in sec " . $stopWatch->split()); + $ctx->customer = $customer; - if (!$verifyOnly) { + $doorLog->id_customer = $customer->id_customer; + \Yii::info("$ctx->requestId: customer {$customer->id_customer} loaded in sec " . $stopWatch->split()); + + if (!$ctx->verifyOnly) { // save the door log $doorLog->save(false); } - \Yii::info("$requestId: door log {$doorLog->id_door_log} saved in sec " . $stopWatch->split()); + \Yii::info("$ctx->requestId: door log {$doorLog->id_door_log} saved in sec " . $stopWatch->split()); - // if direction is in - if ($direction == DoorLog::$DIRECTION_IN) { + // if direction is in, check if usage count must be increased + if ($ctx->direction == DoorLog::$DIRECTION_IN) { - if ($card->isFlagDoor()) { - throw new BadRequestHttpException("$requestId: Card already 'IN': " . $card->id_card); + if ($ctx->card->isFlagDoor()) { + throw new FitnessException( + "$ctx->requestId: Card already 'IN': " . $ctx->card->id_card, + FitnessException::TYPE_BAD_REQUEST, + "ALREADY_IN", + $ctx + ); } - if ($card->isFlagKey()) { - throw new BadRequestHttpException("$requestId: Key required: " . $card->id_card); + if ($ctx->card->isFlagKey()) { + throw new FitnessException( + "$ctx->requestId: Key required: " . $ctx->card->id_card, + FitnessException::TYPE_BAD_REQUEST, + "REQUIRED_KEY", + $ctx + ); } - if ($card->isFlagStatus()) { - throw new BadRequestHttpException("$requestId: Card has no active status: " . $card->id_card); + if ($ctx->card->isFlagStatus()) { + throw new FitnessException( + "$ctx->requestId: Card has no active status: " . $ctx->card->id_card, + FitnessException::TYPE_BAD_REQUEST, + "CARD_STATUS_NOT_ACTIVE", + $ctx + ); } - if (isset($virtualKey)) { + if (isset($ctx->virtualKey)) { - if (isset($virtualKey->direction_in_at)) { - throw new BadRequestHttpException("$requestId: Virtual key - already moved in: " . $identifier . '/' . $virtualKey->id_card); + if (isset($ctx->virtualKey->direction_in_at)) { + throw new FitnessException( + "$ctx->requestId: Virtual key - already moved in: " . $ctx->identifier . '/' . $ctx->virtualKey->id_card, + FitnessException::TYPE_BAD_REQUEST, + "VIRTUAL_KEY_ALREADY_IN", + $ctx + ); } - $virtualKey->direction_in_at = Helper::getDateTimeString(); - \Yii::info("$requestId: Setting virtual key direction_in_at"); + $ctx->virtualKey->direction_in_at = Helper::getDateTimeString(); + \Yii::info("$ctx->requestId: Setting virtual key direction_in_at"); - if (!$verifyOnly) { - \Yii::info("$requestId: Updating virtual key"); - $virtualKey->save(false); + $ctx->actions[] = "VIRTUAL_KEY_MOVE_IN"; + + if (!$ctx->verifyOnly) { + \Yii::info("$ctx->requestId: Updating virtual key"); + $ctx->virtualKey->save(false); } } // detect if need to increase usage count for ticket - if (!$verifyOnly) { + if (!$ctx->verifyOnly) { // if not verifyonly, check, if ticket usage count must be increased - $countDoorLogsForTicketSince = $this->getCountDoorLogsForTicketSince($ticket->id_ticket, DateUtil::utcDate(clone $date)); - \Yii::info("$requestId: getCountDoorLogsForTicketSince: " . $countDoorLogsForTicketSince); + $countDoorLogsForTicketSince = $this->getCountDoorLogsForTicketSince($ticket->id_ticket, DateUtil::utcDate(clone $ctx->date)); + \Yii::info("$ctx->requestId: getCountDoorLogsForTicketSince: " . $countDoorLogsForTicketSince); if (!isset($countDoorLogsForTicketSince)) { $countDoorLogsForTicketSince = 0; } - \Yii::info("$requestId: count of door logs '{$countDoorLogsForTicketSince}' loaded in sec " . $stopWatch->split()); + \Yii::info("$ctx->requestId: count of door logs '{$countDoorLogsForTicketSince}' loaded in sec " . $stopWatch->split()); // if the current event is the first door log today if ($countDoorLogsForTicketSince == 1) { @@ -311,7 +435,7 @@ class DoorManager extends BaseObject $usageCount = $ticket->usage_count; $ticket->usage_count += 1; $ticket->save(false); - \Yii::info("$requestId: First ticket usage today, increasing usage count for card: " . $card->id_card); + \Yii::info("$ctx->requestId: First ticket usage today, increasing usage count for card: " . $ctx->card->id_card); Log::log( [ 'type' => Log::$TYPE_TICKET_USAGE_FIRST, @@ -320,18 +444,19 @@ class DoorManager extends BaseObject 'id_door_log' => $doorLog->id_door_log ] ); - \Yii::info("$requestId: Ticket usage count increased after first doorlog of day in sec " . $stopWatch->split()); + \Yii::info("$ctx->requestId: Ticket usage count increased after first doorlog of day in sec " . $stopWatch->split()); + $ctx->actions[] = "TICKET_FIRST_USAGE_TODAY"; } else { - \Yii::info("$requestId: more then one door log today for card: " . $card->id_card); + \Yii::info("$ctx->requestId: more then one door log today for card: " . $ctx->card->id_card); // 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); + $startOfDay = DateUtil::utcDate(clone $ctx->date); + $startOfTomorrow = DateUtil::tomorrowStart(clone $ctx->date); $allDoorLogToday = DoorLog::find() ->andWhere(['>=', 'door_log.created_at', DateUtil::formatDateUtc($startOfDay)]) @@ -340,9 +465,9 @@ class DoorManager extends BaseObject ->andWhere(['in', 'direction', [DoorLog::$DIRECTION_IN_WITHOUT_MOVE, DoorLog::$DIRECTION_IN]]) ->orderBy(['door_log.created_at' => SORT_ASC]) ->all(); - \Yii::info("$requestId: All door logs for today loaded in sec " . $stopWatch->split()); + \Yii::info("$ctx->requestId: All door logs for today loaded in sec " . $stopWatch->split()); - \Yii::info("$requestId: allDoorLogToday", print_r($allDoorLogToday, true)); + \Yii::info("$ctx->requestId: allDoorLogToday", print_r($allDoorLogToday, true)); if (isset($allDoorLogToday) && count($allDoorLogToday) > 0) { @@ -351,7 +476,7 @@ class DoorManager extends BaseObject if (isset($firstInToday)) { - \Yii::info("$requestId: first in today for card: " . $card->id_card . " was at " . $firstInToday->created_at); + \Yii::info("$ctx->requestId: first in today for card: " . $ctx->card->id_card . " was at " . $firstInToday->created_at); $firstEntryDateTimeToday = DateUtil::parseDateTime($firstInToday->created_at); @@ -371,9 +496,9 @@ class DoorManager extends BaseObject $intervals[] = $this->createTicketUsageInterval($intervalStart, $startOfTomorrow, $allDoorLogToday, $doorLog); } - $activeInterval = $this->getActiveInterval($intervals, $createdAt); + $activeInterval = $this->getActiveInterval($intervals, $ctx->createdAt); if (!isset($activeInterval)) { - throw new ServerErrorHttpException("$requestId: Active Interval not found"); + throw new ServerErrorHttpException("$ctx->requestId: Active Interval not found"); } $logCountInActiveInterval = count($activeInterval['logs']); @@ -381,82 +506,120 @@ class DoorManager extends BaseObject if ($logCountInActiveInterval == 1) { $ticket->usage_count = $ticket->usage_count + 1; $ticket->save(false); - \Yii::info("$requestId: Ticket usage count increased after first IN after first door_log in interval in sec " . $stopWatch->split()); - + \Yii::info("$ctx->requestId: Ticket usage count increased after first IN after first door_log in interval in sec " . $stopWatch->split()); + $ctx->actions[] = "TICKET_INCREASE_USAGE"; } } } } + $ctx->actions[] = "MOVE_IN"; } - if ($direction == DoorLog::$DIRECTION_OUT || $direction == DoorLog::$DIRECTION_OUT_WITHOUT_MOVE) { + if ($ctx->direction == DoorLog::$DIRECTION_OUT || $ctx->direction == DoorLog::$DIRECTION_OUT_WITHOUT_MOVE) { - if ($card->isFlagOutKey()) { - throw new BadRequestHttpException("$requestId: Can't exit with card has a key assigned"); + if ($ctx->card->isFlagOutKey()) { + throw new FitnessException( + "$ctx->requestId: Can't exit with card has a key assigned", + FitnessException::TYPE_BAD_REQUEST, + "CARD_LOCKER_KEY_ASSIGNED_FLAG", + $ctx + ); } - if ($card->isFlagStatus()) { - throw new BadRequestHttpException("$requestId: Can't exit with card has inactive status"); + if ($ctx->card->isFlagStatus()) { + throw new FitnessException( + "$ctx->requestId: Can't exit with card has inactive status", + FitnessException::TYPE_BAD_REQUEST, + "CARD_STATUS_INACTIVE", + $ctx + ); } - $keyAssigned = CardKeyAssignment::findOne(['id_card' => $card->id_card]); + $keyAssigned = CardKeyAssignment::findOne(['id_card' => $ctx->card->id_card]); if (isset($keyAssigned)) { - throw new BadRequestHttpException("$requestId: Can't exit with card has a key assigned"); + throw new FitnessException( + "$ctx->requestId: Can't exit with card has a key assigned", + FitnessException::TYPE_BAD_REQUEST, + "CARD_LOCKER_KEY_ASSIGNED_KEY", + $ctx + ); } if (isset($virtualKey)) { if (!isset($virtualKey->direction_in_at)) { - throw new BadRequestHttpException("$requestId: Virtual key: move out without move in"); + throw new FitnessException( + "$ctx->requestId: Virtual key: move out without move in", + FitnessException::TYPE_BAD_REQUEST, + "VIRTUAL_KEY_MOVE_OUT_WITHOUT_MOVE_IN", + $ctx + ); } if (isset($virtualKey->direction_out_at)) { - throw new BadRequestHttpException("$requestId: Virtual key: already move out"); + throw new FitnessException( + "$ctx->requestId: Virtual key: already move out", + FitnessException::TYPE_BAD_REQUEST, + "VIRTUAL_KEY_ALREADY_OUT", + $ctx + ); } $virtualKey->direction_out_at = Helper::getDateTimeString(); - if (!$verifyOnly) { + if (!$ctx->verifyOnly) { $virtualKey->save(false); } } $ticket->count_move_out = $ticket->usage_count; - if (!$verifyOnly) { + if (!$ctx->verifyOnly) { $ticket->save(false); } - \Yii::info("$requestId: direction_out: ticket count_move_out set after direction_out in sec " . $stopWatch->split()); + $ctx->actions[] = "MOVE_OUT"; + \Yii::info("$ctx->requestId: direction_out: ticket count_move_out set after direction_out in sec " . $stopWatch->split()); } - if (!$verifyOnly) { + if (!$ctx->verifyOnly) { Card::updateCardFlagTicket($ticket->id_ticket); - \Yii::info("$requestId: updateCardFlagTicket: card flag updated in sec " . $stopWatch->split()); + \Yii::info("$ctx->requestId: updateCardFlagTicket: card flag updated in sec " . $stopWatch->split()); // reload card after flag is set - $card = Card::readCard($cardNumber); - 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::info("$requestId: direction_out: Door flag updated in sec " . $stopWatch->split()); + $ctx->card = Card::readCard($ctx->cardNumber); + if ($ctx->direction == DoorLog::$DIRECTION_OUT_WITHOUT_MOVE || $ctx->direction == DoorLog::$DIRECTION_OUT) { + $ctx->card->flag_out = Helper::setBit($ctx->card->flag_out, Card::$FLAG_DOOR, true); + $ctx->card->flag = Helper::setBit($ctx->card->flag, Card::$FLAG_DOOR, false); + $ctx->card->save(false); + \Yii::info("$ctx->requestId: 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::info("$requestId: direction_in: Card flag updated in sec " . $stopWatch->split()); + } else if ($ctx->direction == DoorLog::$DIRECTION_IN || $ctx->direction == DoorLog::$DIRECTION_IN_WITHOUT_MOVE) { + $ctx->card->flag_out = Helper::setBit($ctx->card->flag_out, Card::$FLAG_DOOR, false); + $ctx->card->flag = Helper::setBit($ctx->card->flag, Card::$FLAG_DOOR, true); + $ctx->card->save(false); + \Yii::info("$ctx->requestId: direction_in: Card flag updated in sec " . $stopWatch->split()); } } $stopWatch->stop(); - \Yii::info("$requestId: finished in sec " . $stopWatch->getTotal()); + \Yii::info("$ctx->requestId: finished in sec " . $stopWatch->getTotal()); \Yii::$app->db->transaction->commit(); - \Yii::info("$requestId: Commited"); + \Yii::info("$ctx->requestId: Commited"); + } catch (FitnessException $fe) { + \Yii::$app->db->transaction->rollBack(); + \Yii::info("$ctx->requestId: rollbacked"); + throw $fe; } catch (\Exception $e) { \Yii::$app->db->transaction->rollBack(); - \Yii::info("$requestId: rollbacked"); - throw $e; + \Yii::info("$ctx->requestId: rollbacked"); + throw new FitnessException( + "UNKNOWN_ERROR", + FitnessException::UNKNOWN_ERROR, + "CUSTOMER_UNKNOWN_ERROR", + $ctx, + $e + + ); } } diff --git a/common/models/DoorCardPass.php b/common/models/DoorCardPass.php index 36ecf7c..46d1b7e 100644 --- a/common/models/DoorCardPass.php +++ b/common/models/DoorCardPass.php @@ -18,6 +18,8 @@ use Yii; * @property integer $id_user * @property integer $created_at * @property integer $updated_at + * @property integer $valid_until_at + * @property integer $invalidated_at * */ class DoorCardPass extends \yii\db\ActiveRecord diff --git a/console/controllers/DoorCardPassController.php b/console/controllers/DoorCardPassController.php index 800d983..471cb13 100644 --- a/console/controllers/DoorCardPassController.php +++ b/console/controllers/DoorCardPassController.php @@ -1,6 +1,7 @@ db->createCommand(Card::SQL_DENY_DOOR_CARD_PASS())->execute(); +// \Yii::$app->db->createCommand(Card::SQL_DENY_DOOR_CARD_PASS())->execute(); + $manager = new DoorCardPassManager(); + $manager->hardReset(); } public function actionAllowAllCard( ) @@ -18,12 +21,10 @@ class DoorCardPassController extends Controller{ \Yii::$app->db->createCommand(Card::SQL_ALLOW_DOOR_CARD_PASS())->execute(); } - public function actionDenyExpiredCard( ) + public function actionInvalidate( ) { - $now = date('Y-m-d H:i:s' ); - $now = time(); - \Yii::$app->db->createCommand(Card::SQL_DENY_DOOR_CARD_PASS_EXPIRED() ,['datetime'=> $now] ) - ->execute(); + $manager = new DoorCardPassManager(); + $manager->updateDoorCardPassStateForAllCard(); } } diff --git a/console/migrations/m230209_200000_alter_table_door_card_pass_add_column_valid_until.php b/console/migrations/m230209_200000_alter_table_door_card_pass_add_column_valid_until.php new file mode 100644 index 0000000..3159dc3 --- /dev/null +++ b/console/migrations/m230209_200000_alter_table_door_card_pass_add_column_valid_until.php @@ -0,0 +1,44 @@ +addColumn("door_card_pass","valid_until_at",$this->dateTime()); + + + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + echo "m230126_202055_create_table_door_card_pass cannot be reverted.\n"; + + return false; + } + + /* + // Use up()/down() to run migration code without a transaction. + public function up() + { + + } + + public function down() + { + echo "m230126_202055_create_table_door_card_pass cannot be reverted.\n"; + + return false; + } + */ +} diff --git a/console/migrations/m230209_200010_alter_table_door_card_pass_add_column_invalidated_at.php b/console/migrations/m230209_200010_alter_table_door_card_pass_add_column_invalidated_at.php new file mode 100644 index 0000000..8886fa8 --- /dev/null +++ b/console/migrations/m230209_200010_alter_table_door_card_pass_add_column_invalidated_at.php @@ -0,0 +1,44 @@ +addColumn("door_card_pass","invalidated_at",$this->dateTime()); + + + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + echo "m230126_202055_create_table_door_card_pass cannot be reverted.\n"; + + return false; + } + + /* + // Use up()/down() to run migration code without a transaction. + public function up() + { + + } + + public function down() + { + echo "m230126_202055_create_table_door_card_pass cannot be reverted.\n"; + + return false; + } + */ +} diff --git a/docker/fitness/.env b/docker/fitness/.env index b7cf2f8..5d4a13c 100644 --- a/docker/fitness/.env +++ b/docker/fitness/.env @@ -1 +1,2 @@ -FITNESS_REST_ALLOW_VERIFY_ONLY=true \ No newline at end of file +FITNESS_REST_ALLOW_VERIFY_ONLY=true +DOOR_ENTRY_STRATEGY=door_pass \ No newline at end of file diff --git a/docker/fitness/docker-compose.yml b/docker/fitness/docker-compose.yml index f35a0f5..d6c26f3 100644 --- a/docker/fitness/docker-compose.yml +++ b/docker/fitness/docker-compose.yml @@ -22,7 +22,8 @@ services: FITNESS_MAIL_PORT: 1025 FITNESS_MAIL_USERNAME: test FITNESS_MAIL_PASSWORD: test - FITNESS_REST_ALLOW_VERIFY_ONLY: $FITNESS_REST_ALLOW_VERIFY_ONLY + FITNESS_REST_ALLOW_VERIFY_ONLY: "$FITNESS_REST_ALLOW_VERIFY_ONLY" + DOOR_ENTRY_STRATEGY: "$DOOR_ENTRY_STRATEGY" cutlerdb: image: mariadb:10.1 diff --git a/frontend/controllers/DoorCardPassController.php b/frontend/controllers/DoorCardPassController.php index 55479af..63d9c71 100644 --- a/frontend/controllers/DoorCardPassController.php +++ b/frontend/controllers/DoorCardPassController.php @@ -2,18 +2,17 @@ namespace frontend\controllers; +use common\components\DateUtil; +use common\components\Helper; +use common\manager\DoorCardPassManager; use common\models\Card; use common\models\DoorCardPass; use Yii; use common\models\City; -use backend\models\CitySearch; use yii\web\BadRequestHttpException; use yii\web\Controller; use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; -use yii\base\BaseObject; -use yii\db\Query; -use yii\helpers\Json; /** * CityController implements the CRUD actions for City model. @@ -39,11 +38,12 @@ class DoorCardPassController extends Controller if (!isset($card)){ throw new BadRequestHttpException("card id not found"); } - $model = new DoorCardPass(); - $model->id_card = $idCard; - $model->save(false); - $this->redirect('customer/reception'); + $doorCardPassManager = new DoorCardPassManager(); + $doorCardPassManager->createNewDoorCardPass($idCard); + $doorCardPassManager->updateDoorCardPassStateForCard($idCard); + + $this->redirect(['customer/reception', 'number' => $card->number]); } /** @@ -72,5 +72,5 @@ class DoorCardPassController extends Controller throw new NotFoundHttpException('The requested page does not exist.'); } } - + } diff --git a/frontend/views/common/door_entry_strategy/_strategy_door_pass.php b/frontend/views/common/door_entry_strategy/_strategy_door_pass.php index d1f8848..dc9c8e3 100644 --- a/frontend/views/common/door_entry_strategy/_strategy_door_pass.php +++ b/frontend/views/common/door_entry_strategy/_strategy_door_pass.php @@ -10,8 +10,53 @@ use frontend\components\HtmlHelper; /* @var $model frontend\models\ReceptionForm */ /* @var $form yii\widgets\ActiveForm */ ?> - ['door-card-pass/allow', 'number' => $model->getCardNumber()], +card))){ + return; +} + $activeCardPasses = \common\models\DoorCardPass::find() + ->andWhere( + [ + 'id_card' => $model->card->id_card, + 'invalidated_at' => null , + +// [ '>=', $field , $start ] + + + ] + )->andWhere( + ['>=', 'valid_until_at', \common\components\DateUtil::formatDateTimeUtc(\common\components\DateUtil::utcDateTime())], + ) + ->orderBy(['valid_until_at' => SORT_DESC ])->all(); + + if (count($activeCardPasses) > 0){ + $cardPass = $activeCardPasses[0]; + ?> +
+ +
+ +
+ valid_until_at ; + ?> +
+ +
+ Nincs érvényes forgó kapu belépő +
+ ['door-card-pass/allow-card', 'idCard' => $model->card->id_card], 'method' => 'post', ]); ?>