cutler movar doormove
This commit is contained in:
parent
a4ee6ddd40
commit
30dca92465
@ -204,4 +204,8 @@ class DateUtil
|
|||||||
return $dateTime->add(new \DateInterval('PT' . $minutes . 'M'));
|
return $dateTime->add(new \DateInterval('PT' . $minutes . 'M'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function minusMinutes($dateTime, $minutes){
|
||||||
|
return $dateTime->sub(new \DateInterval('PT' . $minutes . 'M'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
729
common/manager/DoorCardPassDoorManager.php
Normal file
729
common/manager/DoorCardPassDoorManager.php
Normal file
@ -0,0 +1,729 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
use common\models\CardKeyAssignment;
|
||||||
|
use common\models\DoorLog;
|
||||||
|
use common\models\DoorLogForTest;
|
||||||
|
use common\models\DoorManagerLog;
|
||||||
|
use common\models\Key;
|
||||||
|
use common\models\Log;
|
||||||
|
use common\models\Ticket;
|
||||||
|
use common\models\VirtualKey;
|
||||||
|
use frontend\models\KeyToggleForm;
|
||||||
|
use yii\base\BaseObject;
|
||||||
|
use yii\base\InvalidConfigException;
|
||||||
|
use yii\db\Exception;
|
||||||
|
use yii\web\BadRequestHttpException;
|
||||||
|
use yii\web\ServerErrorHttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by IntelliJ IDEA.
|
||||||
|
* User: rocho
|
||||||
|
* Date: 2018.12.17.
|
||||||
|
* Time: 6:12
|
||||||
|
*/
|
||||||
|
class DoorCardPassDoorManager extends BaseObject
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $identifier 00000000: nyomogombos nyitás
|
||||||
|
* @param $device string B(gomb)|Q(qrcode)|C(nfc),E(emergency)
|
||||||
|
* @param $direction string IN|OUT
|
||||||
|
* @param $verifyOnly boolean true: akkor csak lekerdezés, false: megtörtént a mozgás (ilyenkor vonunk le egy alkalmat)
|
||||||
|
* @param $createdAt number unix timestamp , override createdAt for doorLogs
|
||||||
|
* @param $date number unix timestamp, override date for validation check 'now'
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function move($identifier, $device, $direction, $verifyOnly, $createdAt = null, $date = null)
|
||||||
|
{
|
||||||
|
$requestId = uniqid("", false);
|
||||||
|
$context = new DoorMoveContext([
|
||||||
|
'requestId' => $requestId,
|
||||||
|
'identifier' => $identifier,
|
||||||
|
'device' => $device,
|
||||||
|
'direction' => $direction,
|
||||||
|
'originalDirection' => $direction,
|
||||||
|
'verifyOnly' => $verifyOnly
|
||||||
|
]);
|
||||||
|
try {
|
||||||
|
$stopWatch = new StopWatch();
|
||||||
|
|
||||||
|
|
||||||
|
\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->createdAt = $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;
|
||||||
|
|
||||||
|
switch ($direction) {
|
||||||
|
case 'IN':
|
||||||
|
$direction = DoorLog::$DIRECTION_IN;
|
||||||
|
break;
|
||||||
|
case 'OUT':
|
||||||
|
$direction = DoorLog::$DIRECTION_OUT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new FitnessException(
|
||||||
|
"Direction $direction not supported",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"INVALID_DIRECTION",
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$context->direction = $direction;
|
||||||
|
|
||||||
|
// if device is qr code
|
||||||
|
if ($device == 'Q') {
|
||||||
|
|
||||||
|
// allow only virtual key
|
||||||
|
$virtualKey = VirtualKey::findOne(['number' => $identifier]);
|
||||||
|
|
||||||
|
if (!isset($virtualKey)) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"Virtual Key Not Found",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"VIRTUAL_KEY_NOT_FOUND",
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$card = Card::findOne($virtualKey->id_card);
|
||||||
|
|
||||||
|
if ($card == null) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"Virtual Key Not Found",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"CARD_FOR_VIRTUAL_KEY_NOT_FOUND",
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$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 FitnessException(
|
||||||
|
"Virtual Key Not Found",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"CARD_NOT_FOUND",
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
\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;
|
||||||
|
|
||||||
|
if ( $card->status != Card::STATUS_ACTIVE){
|
||||||
|
throw new FitnessException(
|
||||||
|
"Card is not active",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"CARD_NOT_ACTIVE",
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
\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;
|
||||||
|
|
||||||
|
if ($e->type == FitnessException::TYPE_BAD_REQUEST) {
|
||||||
|
throw new BadRequestHttpException($e->getMessage());
|
||||||
|
} else {
|
||||||
|
throw new ServerErrorHttpException($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$context->error = true;
|
||||||
|
$context->exception = $e;
|
||||||
|
$context->errorCode = "UNKNOWN";
|
||||||
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
// do logging
|
||||||
|
$this->logContext($context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $ctx DoorMoveContext
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function logContext($ctx)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$result = [
|
||||||
|
//datetime $updated_at --
|
||||||
|
//datetime $created_at --
|
||||||
|
//string $ticket_usage
|
||||||
|
//string $request_id
|
||||||
|
'request_id' => $ctx->requestId,
|
||||||
|
//string $identifier
|
||||||
|
'identifier' => $ctx->identifier,
|
||||||
|
//bool $verify_only
|
||||||
|
'verify_only' => $ctx->verifyOnly,
|
||||||
|
//string $device
|
||||||
|
'device' => $ctx->device,
|
||||||
|
//string $direction
|
||||||
|
'direction' => $ctx->direction,
|
||||||
|
//string $original_direction
|
||||||
|
'original_direction' => $ctx->originalDirection,
|
||||||
|
//integer $card_id_card
|
||||||
|
'card_id_card' => isset($ctx->card) ? $ctx->card->id_card : null,
|
||||||
|
//string $card_number
|
||||||
|
'card_number' => isset($ctx->card) ? $ctx->card->number : null,
|
||||||
|
//string $ticket_id_ticket
|
||||||
|
'ticket_id_ticket' => isset($ctx->ticket) ? $ctx->ticket->id_ticket : null,
|
||||||
|
//string $ticket_type_name
|
||||||
|
'ticket_type_name' => isset($ctx->ticket) ? $ctx->ticket->ticketType->name : null,
|
||||||
|
//string $ticket_usage_count
|
||||||
|
'ticket_usage_count' => isset($ctx->ticket) ? $ctx->ticket->usage_count : null,
|
||||||
|
//string $customer_id_customer
|
||||||
|
'customer_id_customer' => isset($ctx->customer) ? $ctx->customer->id_customer : null,
|
||||||
|
//string $customer_name
|
||||||
|
'customer_name' => isset($ctx->customer) ? $ctx->customer->name : null,
|
||||||
|
//string $customer_email
|
||||||
|
'customer_email' => isset($ctx->customer) ? $ctx->customer->email : null,
|
||||||
|
//integer $key_id_key
|
||||||
|
'key_id_key' => isset($ctx->key) ? $ctx->key->id_key : null,
|
||||||
|
//string $key_number
|
||||||
|
'key_number' => isset($ctx->key) ? $ctx->key->number : null,
|
||||||
|
//integer $virtual_key_id
|
||||||
|
'virtual_key_id' => isset($ctx->virtualKey) ? $ctx->virtualKey->id : null,
|
||||||
|
//string $validation_kind
|
||||||
|
'validation_kind' => $ctx->kind,
|
||||||
|
//bool $error
|
||||||
|
'error' => $ctx->error,
|
||||||
|
//string $error_code
|
||||||
|
'error_code' => $ctx->errorCode,
|
||||||
|
'ticket_usage' => $ctx->increasedTicketUsageCount,
|
||||||
|
// 'actions' => implode(",",$ctx->actions),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($ctx->error === true) {
|
||||||
|
if (isset($ctx->exception)) {
|
||||||
|
// string $error_message
|
||||||
|
$result['error_message'] = substr($ctx->exception->getMessage(), 0, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\Yii::info("door log: " . implode(";", $result));
|
||||||
|
|
||||||
|
\Yii::$app->db->beginTransaction();
|
||||||
|
$log = new DoorManagerLog($result);
|
||||||
|
$log->save(false);
|
||||||
|
\Yii::$app->db->transaction->commit();
|
||||||
|
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Yii::error("Failed to log door context:" . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveEmergency($ctx)
|
||||||
|
{
|
||||||
|
$ctx->kind = "EMERGENCY";
|
||||||
|
\Yii::info("$ctx->requestId: emergency move");
|
||||||
|
try {
|
||||||
|
$createdAtStr = DateUtil::formatDateTimeUtc($ctx->createdAt);
|
||||||
|
\Yii::$app->db->beginTransaction();
|
||||||
|
|
||||||
|
$doorLog = new DoorLog();
|
||||||
|
$doorLog->version = 2;
|
||||||
|
$doorLog->direction = DoorLog::$DIRECTION_ALL_EMERGENCY;
|
||||||
|
$doorLog->source_app = $ctx->device;
|
||||||
|
$doorLog->created_at = $createdAtStr;
|
||||||
|
|
||||||
|
$doorLog->save(false);
|
||||||
|
Log::log(
|
||||||
|
[
|
||||||
|
'type' => Log::$TYPE_INFO,
|
||||||
|
'message' => 'Ajtó nyitás: vészhelyzet',
|
||||||
|
'id_door_log' => $doorLog->id_door_log
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$ctx->actions[] = "EMERGENCY_MOVE";
|
||||||
|
|
||||||
|
\Yii::$app->db->transaction->commit();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Yii::$app->db->transaction->rollBack();
|
||||||
|
throw new FitnessException(
|
||||||
|
"unknowon error",
|
||||||
|
FitnessException::UNKNOWN_ERROR,
|
||||||
|
"EMERGENCY_OPEN_FAILED",
|
||||||
|
$ctx,
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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($ctx->createdAt);
|
||||||
|
\Yii::$app->db->beginTransaction();
|
||||||
|
|
||||||
|
$doorLog = new DoorLog();
|
||||||
|
$doorLog->version = 2;
|
||||||
|
$doorLog->direction = $ctx->direction;
|
||||||
|
$doorLog->source_app = $ctx->device;
|
||||||
|
$doorLog->created_at = $createdAtStr;
|
||||||
|
$doorLog->id_card = $ctx->card->id_card;
|
||||||
|
$doorLog->card_flag = $ctx->card->flag;
|
||||||
|
|
||||||
|
if (!$ctx->verifyOnly) {
|
||||||
|
|
||||||
|
$doorLog->save(false);
|
||||||
|
Log::log(
|
||||||
|
[
|
||||||
|
'type' => Log::$TYPE_INFO,
|
||||||
|
'message' => 'Ajtó nyitás: munkatárs',
|
||||||
|
'id_door_log' => $doorLog->id_door_log
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ctx->actions[] = "EMPLOYEE_" . $ctx->originalDirection;
|
||||||
|
|
||||||
|
\Yii::$app->db->transaction->commit();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Yii::$app->db->transaction->rollBack();
|
||||||
|
throw new FitnessException(
|
||||||
|
"unknowon error",
|
||||||
|
FitnessException::UNKNOWN_ERROR,
|
||||||
|
"EMPLOYEE_FAILED",
|
||||||
|
$ctx,
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws BadRequestHttpException
|
||||||
|
* @throws ServerErrorHttpException
|
||||||
|
* @throws InvalidConfigException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
function moveCustomer($ctx/* $requestId, $identifier, $device, $direction, $verifyOnly, $createdAt, $date, $card, $cardNumber, $virtualKey*/)
|
||||||
|
{
|
||||||
|
$ctx->kind = "CUSTOMER";
|
||||||
|
\Yii::info("$ctx->requestId: move customer");
|
||||||
|
$stopWatch = new StopWatch();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$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 = $ctx->direction;
|
||||||
|
$doorLog->source_app = $ctx->device;
|
||||||
|
$doorLog->created_at = $createdAtStr;
|
||||||
|
$doorLog->id_card = $ctx->card->id_card;
|
||||||
|
$doorLog->card_flag = $ctx->card->flag;
|
||||||
|
|
||||||
|
|
||||||
|
$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) {
|
||||||
|
$ticket = $activeTickets[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// active ticket is required
|
||||||
|
if (!isset($ticket)) {
|
||||||
|
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("$ctx->requestId: ticket {$ticket->id_ticket} loaded in sec " . $stopWatch->split());
|
||||||
|
|
||||||
|
$doorLog->id_ticket_current = $ticket->id_ticket;
|
||||||
|
|
||||||
|
// customer is required
|
||||||
|
$customer = $ctx->card->customer;
|
||||||
|
if (!isset($customer)) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"$ctx->requestId: Customer not found for:" . $ctx->card->number,
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"NOT_FOUND_CUSTOMER",
|
||||||
|
$ctx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ctx->customer = $customer;
|
||||||
|
|
||||||
|
$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("$ctx->requestId: door log {$doorLog->id_door_log} saved in sec " . $stopWatch->split());
|
||||||
|
|
||||||
|
// if direction is in, check if usage count must be increased
|
||||||
|
if ($ctx->direction == DoorLog::$DIRECTION_IN) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual key must exist
|
||||||
|
*/
|
||||||
|
if (!isset($ctx->virtualKey)) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"$ctx->requestId: Customer not found for:" . $ctx->card->number,
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"NOT_FOUND_CUSTOMER",
|
||||||
|
$ctx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: virtual key created_at validation
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$ctx->actions[] = "VIRTUAL_KEY_MOVE_IN";
|
||||||
|
|
||||||
|
// detect if need to increase usage count for ticket
|
||||||
|
if (!$ctx->verifyOnly) {
|
||||||
|
|
||||||
|
$ctx->virtualKey->direction_in_at = Helper::getDateTimeString();
|
||||||
|
\Yii::info("$ctx->requestId: Setting virtual key direction_in_at");
|
||||||
|
\Yii::info("$ctx->requestId: Updating virtual key");
|
||||||
|
$ctx->virtualKey->save(false);
|
||||||
|
|
||||||
|
$allDoorLogToday = DoorManagerLog::findAllEntryForTicketFromTime($ctx->ticket->id_ticket);
|
||||||
|
$countDoorLogsForTicketSince = count($allDoorLogToday);
|
||||||
|
|
||||||
|
// if the current event is the first door log today
|
||||||
|
if ($countDoorLogsForTicketSince == 0) {
|
||||||
|
// increase the ticket usage count with 1
|
||||||
|
$usageCount = $ticket->usage_count;
|
||||||
|
$ctx->increasedTicketUsageCount = true;
|
||||||
|
$ticket->usage_count += 1;
|
||||||
|
$ticket->save(false);
|
||||||
|
\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,
|
||||||
|
'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::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("$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
|
||||||
|
|
||||||
|
|
||||||
|
if (count($allDoorLogToday) == 0) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"$ctx->requestId: Tried to inc ticket usage without prev entry: " . $ctx->identifier . '/' . $ctx->virtualKey->id_card,
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"INVALID_TICKET_USAGE_INCREMENT",
|
||||||
|
$ctx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$firstInToday = $allDoorLogToday[0];
|
||||||
|
\Yii::info("$ctx->requestId: first in today for card: " . $ctx->card->id_card . " was at " . $firstInToday->created_at);
|
||||||
|
$firstEntryDateTimeToday = DateUtil::parseDateTime($firstInToday->created_at);
|
||||||
|
$interval = \DateInterval::createFromDateString('3 hours');
|
||||||
|
$startOfTomorrow = DateUtil::tomorrowStart(clone $ctx->date);
|
||||||
|
$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, $ctx->createdAt);
|
||||||
|
if (!isset($activeInterval)) {
|
||||||
|
throw new ServerErrorHttpException("$ctx->requestId: Active Interval not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$logCountInActiveInterval = count($activeInterval['logs']);
|
||||||
|
|
||||||
|
if ($logCountInActiveInterval == 1) {
|
||||||
|
$ctx->increasedTicketUsageCount = true;
|
||||||
|
$ticket->usage_count = $ticket->usage_count + 1;
|
||||||
|
$ticket->save(false);
|
||||||
|
\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 ($ctx->direction == DoorLog::$DIRECTION_OUT || $ctx->direction == DoorLog::$DIRECTION_OUT_WITHOUT_MOVE) {
|
||||||
|
|
||||||
|
// virtual key must exist
|
||||||
|
if (!isset($ctx->virtualKey)) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"$ctx->requestId: Virtual key: move out without virtual key",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"MOVE_OUT_WITHOUT_VIRTUAL_KEY",
|
||||||
|
$ctx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual must already be in
|
||||||
|
if (!isset($ctx->virtualKey->direction_in_at)) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"$ctx->requestId: Virtual key: move out without move in",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"VIRTUAL_KEY_MOVE_OUT_WITHOUT_MOVE_IN",
|
||||||
|
$ctx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual key must not be already out
|
||||||
|
if (isset($ctx->virtualKey->direction_out_at)) {
|
||||||
|
throw new FitnessException(
|
||||||
|
"$ctx->requestId: Virtual key: already move out",
|
||||||
|
FitnessException::TYPE_BAD_REQUEST,
|
||||||
|
"VIRTUAL_KEY_ALREADY_OUT",
|
||||||
|
$ctx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!$ctx->verifyOnly) {
|
||||||
|
// set out date
|
||||||
|
$ctx->virtualKey->direction_out_at = Helper::getDateTimeString();
|
||||||
|
$ctx->virtualKey->save(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!$ctx->verifyOnly) {
|
||||||
|
$ticket->count_move_out = $ticket->usage_count;
|
||||||
|
$ticket->save(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ctx->actions[] = "MOVE_OUT";
|
||||||
|
\Yii::info("$ctx->requestId: direction_out: ticket count_move_out set after direction_out in sec " . $stopWatch->split());
|
||||||
|
}
|
||||||
|
|
||||||
|
$stopWatch->stop();
|
||||||
|
\Yii::info("$ctx->requestId: finished in sec " . $stopWatch->getTotal());
|
||||||
|
\Yii::$app->db->transaction->commit();
|
||||||
|
\Yii::info("$ctx->requestId: Commited");
|
||||||
|
} catch (FitnessException $fe) {
|
||||||
|
$this->doRollback();
|
||||||
|
\Yii::info("$ctx->requestId: rollbacked");
|
||||||
|
throw $fe;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->doRollback();
|
||||||
|
\Yii::info("$ctx->requestId: rollbacked");
|
||||||
|
throw new FitnessException(
|
||||||
|
"UNKNOWN_ERROR",
|
||||||
|
FitnessException::UNKNOWN_ERROR,
|
||||||
|
"CUSTOMER_UNKNOWN_ERROR",
|
||||||
|
$ctx,
|
||||||
|
$e
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doRollback()
|
||||||
|
{
|
||||||
|
if (isset(\Yii::$app->db->transaction) && \Yii::$app->db->transaction->isActive) {
|
||||||
|
\Yii::$app->db->transaction->rollBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveInterval($intervals, $date)
|
||||||
|
{
|
||||||
|
foreach ($intervals as $interval) {
|
||||||
|
$start = $interval['start'];
|
||||||
|
$end = $interval['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) {
|
||||||
|
$createdAt = DateUtil::parseDateTime($log->created_at);
|
||||||
|
if ($createdAt >= $start && $createdAt < $end) {
|
||||||
|
$result['logs'][] = $log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function readActive($cardNumber)
|
||||||
|
{
|
||||||
|
$card = Card::readCard($cardNumber);
|
||||||
|
return Ticket::readActive($card);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return DoorLog::findAll(
|
||||||
|
['id_card' => $cardNumber]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInfo($cardNumber)
|
||||||
|
{
|
||||||
|
$card = Card::readCard($cardNumber);
|
||||||
|
return [
|
||||||
|
'card' => $card,
|
||||||
|
'customer' => $card->customer,
|
||||||
|
'tickets' => Ticket::readActive($card),
|
||||||
|
'doorLogs' => DoorLog::findAll(
|
||||||
|
['id_card' => $card->id_card]
|
||||||
|
),
|
||||||
|
'lastDoorLog' => DoorLog::find()->orderBy(['id_door_log' => SORT_DESC])->limit(1)->one(),
|
||||||
|
'doorLogCount' => DoorLog::find()->count()
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createLog()
|
||||||
|
{
|
||||||
|
\Yii::info("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::info("Door log saving:" . $log->created_at);
|
||||||
|
$log->save(false);
|
||||||
|
return $log;
|
||||||
|
} else {
|
||||||
|
throw new BadRequestHttpException(print_r($log->getErrors(), true));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
\Yii::info("validated" . print_r($log->errors, true));
|
||||||
|
throw new BadRequestHttpException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ use yii\web\ServerErrorHttpException;
|
|||||||
* Date: 2018.12.17.
|
* Date: 2018.12.17.
|
||||||
* Time: 6:12
|
* Time: 6:12
|
||||||
*/
|
*/
|
||||||
class DoorManager extends BaseObject
|
class KeyDoorManager extends BaseObject
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,5 +63,22 @@ class DoorManagerLog extends \yii\db\ActiveRecord
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static function findFirstForCardFromTime($idCard,$datetime){
|
||||||
|
return DoorManagerLog::find()
|
||||||
|
->andWhere(['id_card' =>$idCard])
|
||||||
|
->andWhere(['verify_only' => false])
|
||||||
|
->andWhere(['>=' ,'created_at', $datetime])
|
||||||
|
->one();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static function findAllEntryForTicketFromTime($idTicket, $datetime = null){
|
||||||
|
return DoorManagerLog::find()
|
||||||
|
->andWhere(['id_ticket' =>$idTicket])
|
||||||
|
->andWhere(['verify_only' => false])
|
||||||
|
->andWhere(['direction' => 'IN'])
|
||||||
|
->andWhere(['>=' ,'created_at', isset( $datetime ) ? $datetime : date('Y-m-d H:i:s')])
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace common\models;
|
namespace common\models;
|
||||||
|
|
||||||
|
use common\components\DateUtil;
|
||||||
|
use common\components\Helper;
|
||||||
|
use common\helpers\AppDateTimeHelper;
|
||||||
use Yii;
|
use Yii;
|
||||||
use yii\behaviors\TimestampBehavior;
|
use yii\behaviors\TimestampBehavior;
|
||||||
use yii\helpers\ArrayHelper;
|
use yii\helpers\ArrayHelper;
|
||||||
@ -62,10 +65,27 @@ class VirtualKey extends \yii\db\ActiveRecord
|
|||||||
[
|
[
|
||||||
'class' => TimestampBehavior::className(),
|
'class' => TimestampBehavior::className(),
|
||||||
'value' => function () {
|
'value' => function () {
|
||||||
return date('Y-m-d H:i:s');
|
return DateUtil::formatDateTimeUtc(DateUtil::utcDateTime() );
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
parent::behaviors());
|
parent::behaviors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function readNotInCreatedInTheLastXMinutesByIdCard($idCard,$minutes){
|
||||||
|
|
||||||
|
$date = DateUtil::utcDateTime();
|
||||||
|
$date = DateUtil::minusMinutes($date,$minutes );
|
||||||
|
$dateStr = DateUtil::formatDateTimeUtc($date);
|
||||||
|
|
||||||
|
$virtualKeysNotInCreatedInTheLastXMinuteForCard = VirtualKey::find()
|
||||||
|
->andWhere(['id_card' => $idCard])
|
||||||
|
->andWhere([">=" ,'created_at',$dateStr])
|
||||||
|
->andWhere(['direction_in_at' => null])
|
||||||
|
->orderBy(['created_at' => SORT_DESC])
|
||||||
|
->all();
|
||||||
|
return $virtualKeysNotInCreatedInTheLastXMinuteForCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
FITNESS_REST_ALLOW_VERIFY_ONLY=true
|
FITNESS_REST_ALLOW_VERIFY_ONLY=true
|
||||||
DOOR_ENTRY_STRATEGY=door_pass111
|
DOOR_ENTRY_STRATEGY=door_pass
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace frontend\controllers;
|
namespace frontend\controllers;
|
||||||
|
|
||||||
use common\manager\DoorManager;
|
use common\manager\KeyDoorManager;
|
||||||
use common\models\DoorManagerLog;
|
use common\models\DoorManagerLog;
|
||||||
use frontend\models\DoorManagerLogSearch;
|
use frontend\models\DoorManagerLogSearch;
|
||||||
use frontend\models\InventorySearch;
|
use frontend\models\InventorySearch;
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use common\models\VirtualKey;
|
||||||
use yii\helpers\Html;
|
use yii\helpers\Html;
|
||||||
use yii\widgets\ActiveForm;
|
use yii\widgets\ActiveForm;
|
||||||
use yii\helpers\Url;
|
|
||||||
use frontend\components\HtmlHelper;
|
|
||||||
/* @var $this yii\web\View */
|
/* @var $this yii\web\View */
|
||||||
/* @var $card common\models\Card */
|
/* @var $card common\models\Card */
|
||||||
/* @var $customer common\models\Customer */
|
/* @var $customer common\models\Customer */
|
||||||
@ -15,23 +14,10 @@ use frontend\components\HtmlHelper;
|
|||||||
if (! ( isset($model) && isset($model->card))){
|
if (! ( isset($model) && isset($model->card))){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$activeCardPasses = \common\models\DoorCardPass::find()
|
$virtualKeysNotInCreatedInTheLastXMinuteForCard = VirtualKey::readNotInCreatedInTheLastXMinutesByIdCard($model->card->id_card,5);
|
||||||
->andWhere(
|
|
||||||
[
|
|
||||||
'id_card' => $model->card->id_card,
|
|
||||||
'invalidated_at' => null ,
|
|
||||||
|
|
||||||
// [ '>=', $field , $start ]
|
if (count($virtualKeysNotInCreatedInTheLastXMinuteForCard) > 0){
|
||||||
|
$activeVirtualKey = $virtualKeysNotInCreatedInTheLastXMinuteForCard[0];
|
||||||
|
|
||||||
]
|
|
||||||
)->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];
|
|
||||||
?>
|
?>
|
||||||
<div>
|
<div>
|
||||||
<?php
|
<?php
|
||||||
@ -42,7 +28,7 @@ if (! ( isset($model) && isset($model->card))){
|
|||||||
?>
|
?>
|
||||||
<div>
|
<div>
|
||||||
<?php
|
<?php
|
||||||
echo $cardPass->valid_until_at ;
|
echo $activeVirtualKey->created_at ;
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace rest\controllers;
|
namespace rest\controllers;
|
||||||
|
|
||||||
use common\manager\DoorManager;
|
use common\manager\DoorCardPassDoorManager;
|
||||||
|
use common\manager\KeyDoorManager;
|
||||||
use rest\models\DoorMoveForm;
|
use rest\models\DoorMoveForm;
|
||||||
use Yii;
|
use Yii;
|
||||||
use yii\web\BadRequestHttpException;
|
use yii\web\BadRequestHttpException;
|
||||||
@ -18,25 +19,39 @@ class DoorController extends RestController
|
|||||||
$keys = array_keys($formModel->getFirstErrors());
|
$keys = array_keys($formModel->getFirstErrors());
|
||||||
throw new BadRequestHttpException("Invalid move request:". $formModel->getFirstErrors()[$keys[0]]);
|
throw new BadRequestHttpException("Invalid move request:". $formModel->getFirstErrors()[$keys[0]]);
|
||||||
}
|
}
|
||||||
$doorManager = new DoorManager();
|
$doorManager = new KeyDoorManager();
|
||||||
|
$doorManager->move($formModel->cardNumber,$formModel->device,$formModel->direction, $formModel->validateOnly, $formModel->createdAt, $formModel->date);
|
||||||
|
}
|
||||||
|
return $formModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionMoveMovar()
|
||||||
|
{
|
||||||
|
$formModel = new DoorMoveForm();
|
||||||
|
if ($formModel->load(Yii::$app->request->post(),"") ) {
|
||||||
|
if ( !$formModel->validate()){
|
||||||
|
$keys = array_keys($formModel->getFirstErrors());
|
||||||
|
throw new BadRequestHttpException("Invalid move request:". $formModel->getFirstErrors()[$keys[0]]);
|
||||||
|
}
|
||||||
|
$doorManager = new DoorCardPassDoorManager();
|
||||||
$doorManager->move($formModel->cardNumber,$formModel->device,$formModel->direction, $formModel->validateOnly, $formModel->createdAt, $formModel->date);
|
$doorManager->move($formModel->cardNumber,$formModel->device,$formModel->direction, $formModel->validateOnly, $formModel->createdAt, $formModel->date);
|
||||||
}
|
}
|
||||||
return $formModel;
|
return $formModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function actionReset($cardNumber){
|
public function actionReset($cardNumber){
|
||||||
$doorManager = new DoorManager();
|
$doorManager = new KeyDoorManager();
|
||||||
$doorManager->resetLogs($cardNumber);
|
$doorManager->resetLogs($cardNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function actionInfo($cardNumber){
|
public function actionInfo($cardNumber){
|
||||||
$doorManager = new DoorManager();
|
$doorManager = new KeyDoorManager();
|
||||||
return $doorManager->getInfo($cardNumber);
|
return $doorManager->getInfo($cardNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function actionCreateDoorLog(){
|
public function actionCreateDoorLog(){
|
||||||
$doorManager = new DoorManager();
|
$doorManager = new KeyDoorManager();
|
||||||
return $doorManager->createLog();
|
return $doorManager->createLog();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,12 +61,12 @@ class DoorController extends RestController
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function actionCheckoutKey($cardNumber, $keyNumber){
|
public function actionCheckoutKey($cardNumber, $keyNumber){
|
||||||
$doorManager = new DoorManager();
|
$doorManager = new KeyDoorManager();
|
||||||
$doorManager->checkoutKey($cardNumber,$keyNumber);
|
$doorManager->checkoutKey($cardNumber,$keyNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function actionRevokeKey($cardNumber, $keyNumber){
|
public function actionRevokeKey($cardNumber, $keyNumber){
|
||||||
$doorManager = new DoorManager();
|
$doorManager = new KeyDoorManager();
|
||||||
$doorManager->revokeKey($cardNumber,$keyNumber);
|
$doorManager->revokeKey($cardNumber,$keyNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user