소스 검색

[Administration] Support : finalisation système de tickets

feature/souke
Guillaume Bourgeois 1 년 전
부모
커밋
97b79ee79e
19개의 변경된 파일575개의 추가작업 그리고 115개의 파일을 삭제
  1. +87
    -0
      backend/controllers/SupportAdminController.php
  2. +11
    -6
      backend/controllers/SupportController.php
  3. +32
    -4
      backend/views/layouts/left.php
  4. +12
    -8
      backend/views/support/create.php
  5. +149
    -66
      backend/views/support/index.php
  6. +15
    -6
      backend/views/support/view.php
  7. +22
    -0
      backend/web/css/screen.css
  8. +25
    -0
      backend/web/sass/support/_index.scss
  9. +7
    -0
      common/components/View.php
  10. +1
    -0
      common/config/main.php
  11. +25
    -7
      common/logic/AbstractRepository.php
  12. +8
    -1
      common/logic/Ticket/Ticket/Model/Ticket.php
  13. +12
    -8
      common/logic/Ticket/Ticket/Model/TicketSearch.php
  14. +20
    -3
      common/logic/Ticket/Ticket/Repository/TicketRepository.php
  15. +32
    -0
      common/logic/Ticket/Ticket/Service/TicketBuilder.php
  16. +64
    -2
      common/logic/Ticket/Ticket/Service/TicketSolver.php
  17. +12
    -1
      common/logic/Ticket/TicketUser/Repository/TicketUserRepository.php
  18. +14
    -0
      common/logic/Ticket/TicketUser/Repository/TicketUserRepositoryQuery.php
  19. +27
    -3
      common/logic/Ticket/TicketUser/Service/TicketUserBuilder.php

+ 87
- 0
backend/controllers/SupportAdminController.php 파일 보기

@@ -0,0 +1,87 @@
<?php

/**
* Copyright distrib (2018)
*
* contact@opendistrib.net
*
* Ce logiciel est un programme informatique servant à aider les producteurs
* à distribuer leur production en circuits courts.
*
* Ce logiciel est régi par la licence CeCILL soumise au droit français et
* respectant les principes de diffusion des logiciels libres. Vous pouvez
* utiliser, modifier et/ou redistribuer ce programme sous les conditions
* de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
* sur le site "http://www.cecill.info".
*
* En contrepartie de l'accessibilité au code source et des droits de copie,
* de modification et de redistribution accordés par cette licence, il n'est
* offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
* seule une responsabilité restreinte pèse sur l'auteur du programme, le
* titulaire des droits patrimoniaux et les concédants successifs.
*
* A cet égard l'attention de l'utilisateur est attirée sur les risques
* associés au chargement, à l'utilisation, à la modification et/ou au
* développement et à la reproduction du logiciel par l'utilisateur étant
* donné sa spécificité de logiciel libre, qui peut le rendre complexe à
* manipuler et qui le réserve donc à des développeurs et des professionnels
* avertis possédant des connaissances informatiques approfondies. Les
* utilisateurs sont donc invités à charger et tester l'adéquation du
* logiciel à leurs besoins dans des conditions permettant d'assurer la
* sécurité de leurs systèmes et ou de leurs données et, plus généralement,
* à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
*
* Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
* pris connaissance de la licence CeCILL, et que vous en avez accepté les
* termes.
*/

namespace backend\controllers;

use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\Ticket\Model\TicketSearch;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;

/**
* UserController implements the CRUD actions for User model.
*/
class SupportAdminController extends SupportController
{
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::class,
'actions' => [],
],
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return $this->getUserManager()->isCurrentAdmin();
}
]
],
],
];
}

public function actionIndex()
{
$searchTicket = new TicketSearch();
$dataProviderTicketOpen = $searchTicket->search('admin', ['TicketSearch' => ['status' => Ticket::STATUS_OPEN]]);
$dataProviderTicketClosed = $searchTicket->search('admin', ['TicketSearch' => ['status' => Ticket::STATUS_CLOSED]]);

return $this->render('@backend/views/support/index', [
'context' => 'admin',
'searchTicket' => $searchTicket,
'dataProviderTicketOpen' => $dataProviderTicketOpen,
'dataProviderTicketClosed' => $dataProviderTicketClosed
]);
}

}

+ 11
- 6
backend/controllers/SupportController.php 파일 보기

@@ -38,6 +38,7 @@

namespace backend\controllers;

use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\Ticket\Model\TicketSearch;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
@@ -72,11 +73,14 @@ class SupportController extends BackendController
public function actionIndex()
{
$searchTicket = new TicketSearch();
$dataProviderTicket = $searchTicket->search(\Yii::$app->request->queryParams);
$dataProviderTicketOpen = $searchTicket->search('producer', ['TicketSearch' => ['status' => Ticket::STATUS_OPEN]]);
$dataProviderTicketClosed = $searchTicket->search('producer', ['TicketSearch' => ['status' => Ticket::STATUS_CLOSED]]);

return $this->render('index', [
'context' => 'producer',
'searchTicket' => $searchTicket,
'dataProviderTicket' => $dataProviderTicket
'dataProviderTicketOpen' => $dataProviderTicketOpen,
'dataProviderTicketClosed' => $dataProviderTicketClosed
]);
}

@@ -92,7 +96,7 @@ class SupportController extends BackendController
$this->setFlash('success', 'Le ticket a bien été créé.');
return $this->redirect(['view', 'id' => $ticket->id]);
} else {
return $this->render('create', [
return $this->render('@backend/views/support/create', [
'ticket' => $ticket,
]);
}
@@ -100,15 +104,16 @@ class SupportController extends BackendController

public function actionView(int $id)
{
$ticketManager = $this->getTicketManager();
$ticketMessageManager = $this->getTicketMessageManager();
$ticket = $this->findTicket($id);
$ticketManager->viewTicket($ticket, $this->getUserCurrent());
$ticketMessage = $ticketMessageManager->instanciateTicketMessage($ticket, $this->getUserCurrent());

if ($ticketMessage->load(\Yii::$app->request->post()) && $ticketMessageManager->saveCreate($ticketMessage)) {
if ($ticketMessage->load(\Yii::$app->request->post()) && $ticketManager->createTicketMessage($ticketMessage)) {
return $this->redirect(['view', 'id' => $ticket->id, '#' => 'bottom']);
}

return $this->render('view', [
return $this->render('@backend/views/support/view', [
'ticket' => $ticket,
'ticketMessageResponse' => $ticketMessage
]);

+ 32
- 4
backend/views/layouts/left.php 파일 보기

@@ -37,10 +37,12 @@
*/

use common\helpers\GlobalParam;
use common\logic\Ticket\Ticket\Wrapper\TicketManager;
use yii\helpers\Html;

$producerManager = $this->getProducerManager();
$userManager = $this->getUserManager();
$ticketManager = $this->getTicketManager();

?>

@@ -50,10 +52,23 @@ $userManager = $this->getUserManager();

<?php
$producer = GlobalParam::getCurrentProducer();
$newVersionOpendistribTemplate = '';
$newVersionOpendistribLabel = '';
if ($producer && !$producerManager->isUpToDateWithOpendistribVersion($producer)) {
$newVersionOpendistribTemplate = '<span class="pull-right-container"><small class="label pull-right bg-orange">&nbsp;</small></span>';
$newVersionOpendistribLabel = '<span class="pull-right-container"><small class="label pull-right bg-green">'.GlobalParam::getOpendistribVersion().'</small></span>';
}

$countTicketsProducerUnreadLabel = '';
$countTicketsProducerUnread = $ticketManager->countTicketsUnreadByUser($this->getUserCurrent());
if($countTicketsProducerUnread && !$userManager->isCurrentAdmin()) {
$countTicketsProducerUnreadLabel = '<span class="pull-right-container"><small class="label pull-right bg-green">'.$countTicketsProducerUnread.'</small></span>';
}

$countTicketsAdminUnreadLabel = '';
$countTicketsAdminUnread = $ticketManager->countTicketsAdminUnreadByUser($this->getUserCurrent());
if($countTicketsAdminUnread && $userManager->isCurrentAdmin()) {
$countTicketsAdminUnreadLabel = '<span class="pull-right-container"><small class="label pull-right bg-green">'.$countTicketsAdminUnread.'</small></span>';
}

?>

<?= dmstr\widgets\Menu::widget(
@@ -61,7 +76,13 @@ $userManager = $this->getUserManager();
'options' => ['class' => 'sidebar-menu tree', 'data-widget' => 'tree'],
'items' => [
['label' => "Besoin d'aide ?", 'options' => ['class' => 'header'], 'visible' => $userManager->isCurrentProducer()],
['label' => 'Support', 'icon' => 'comments', 'url' => ['support/index'], 'visible' => $userManager->isCurrentProducer()],
[
'label' => 'Support',
'icon' => 'comments',
'url' => ['support/index'],
'visible' => $userManager->isCurrentProducer(),
'template' => '<a href="{url}">{icon} {label}' . $countTicketsProducerUnreadLabel . '</a>'
],
['label' => $producer->name, 'options' => ['class' => 'header'], 'visible' => $userManager->isCurrentProducer()],
['label' => 'Tableau de bord', 'icon' => 'dashboard', 'url' => ['/site/index'], 'visible' => $userManager->isCurrentProducer()],
['label' => 'Distributions', 'icon' => 'calendar', 'url' => ['/distribution/index'], 'visible' => $userManager->isCurrentProducer()],
@@ -118,11 +139,18 @@ $userManager = $this->getUserManager();
'url' => ['/development/index'],
'visible' => $userManager->isCurrentProducer(),
'active' => Yii::$app->controller->id == 'development',
'template' => '<a href="{url}">{icon} {label}' . $newVersionOpendistribTemplate . '</a>'
'template' => '<a href="{url}">{icon} {label}' . $newVersionOpendistribLabel . '</a>'
],
['label' => 'Tarifs', 'icon' => 'euro', 'url' => ['/producer/billing'], 'visible' => $userManager->isCurrentProducer()],

['label' => 'Administration', 'options' => ['class' => 'header'], 'visible' => $userManager->isCurrentAdmin()],
[
'label' => 'Tickets',
'icon' => 'comments',
'url' => ['support-admin/index'],
'visible' => $userManager->isCurrentAdmin(),
'template' => '<a href="{url}">{icon} {label}' . $countTicketsAdminUnreadLabel . '</a>'
],
['label' => 'Producteurs', 'icon' => 'th-list', 'url' => ['/producer-admin/index'], 'visible' => $userManager->isCurrentAdmin()],
[
'label' => 'Statistiques',

+ 12
- 8
backend/views/support/create.php 파일 보기

@@ -40,17 +40,21 @@ use yii\helpers\Html;
use yii\widgets\ActiveForm;

$this->setTitle('Créer un ticket') ;
$this->addBreadcrumb(['label' => 'ticket', 'url' => ['index']]) ;
$this->addBreadcrumb('Créer') ;
$this->addBreadcrumb(['label' => 'Support', 'url' => ['index']]) ;
$this->addBreadcrumb('Créer un ticket') ;

?>

<div class="ticket-create">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($ticket, 'subject'); ?>
<?= $form->field($ticket, 'message')->textarea(['rows' => 6]); ?>
<div class="form-group">
<?= Html::submitButton('Créer', ['class' => 'btn btn-success']) ?>
<div class="box">
<?php $form = ActiveForm::begin(); ?>
<div class="box-body">
<?= $form->field($ticket, 'subject'); ?>
<?= $form->field($ticket, 'message')->textarea(['rows' => 6]); ?>
</div>
<div class="box-footer">
<?= Html::submitButton('Créer', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
<?php ActiveForm::end(); ?>
</div>

+ 149
- 66
backend/views/support/index.php 파일 보기

@@ -40,15 +40,16 @@ use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\Ticket\Wrapper\TicketManager;
use yii\helpers\Html;
use yii\grid\GridView;
use common\logic\Subscription\Subscription\Model\Subscription;

$ticketManager = TicketManager::getInstance();
$userCurrent = $this->getUserCurrent();
$this->setTitle('Support');
$this->addBreadcrumb($this->getTitle());
//$this->addButton(['label' => 'Créer un ticket <span class="glyphicon glyphicon-plus"></span>', 'url' => 'support/create', 'class' => 'btn btn-primary']) ;

?>

<div class="support-index">
<?php if($context == 'producer'): ?>
<div>
<div class="col-md-4">
<div class="info-box">
@@ -81,72 +82,154 @@ $this->addBreadcrumb($this->getTitle());
</div>
</div>
</div>

<div class="clr"></div>
<?php endif; ?>

<div class="box box-table-tickets">
<div class="box-header with-border">
<h3 class="box-title">Tickets</h3>
</div>

<div class="box-body">
<?= GridView::widget([
'summary' => '',
'filterModel' => $searchTicket,
'dataProvider' => $dataProviderTicket,
'columns' => [
[
'attribute' => 'created_at',
'value' => function ($ticket) {
return date('d/m/Y', strtotime($ticket->created_at));
}
],
[
'attribute' => 'subject',
'format' => 'raw',
'value' => function ($ticket) {
return Html::a($ticket->subject, ['support/view', 'id' => $ticket->id]);
}
],
[
'label' => 'Messages',
'value' => function ($ticket) {
return count($ticket->ticketMessages);
}
],
[
'attribute' => 'status',
'filter' => [Ticket::STATUS_OPEN => 'Ouvert', Ticket::STATUS_CLOSED => 'Fermé'],
'value' => function ($ticket) {
$label = $ticket->status == Ticket::STATUS_OPEN ? 'Ouvert' : 'Fermé';
return $label;
}
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{close-open}',
'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'],
'buttons' => [
'close-open' => function ($url, $ticket) use ($ticketManager) {
if ($ticketManager->isTicketOpen($ticket)) {
$title = 'Fermer';
$url = ['support/close', 'id' => $ticket->id];
$glyphicon = 'glyphicon-folder-close';
} else {
$title = 'Ré-ouvrir';
$url = ['support/open', 'id' => $ticket->id];
$glyphicon = 'glyphicon-folder-open';
}
return Html::a('<span class="glyphicon ' . $glyphicon . '"></span>', $url, [
'title' => $title, 'class' => 'btn btn-default'
]);
}
],
],
],
]); ?>
<div class="nav-tabs-custom ticket-list">
<ul class="nav nav-tabs pull-right">
<li><a href="#tab_2-2" data-toggle="tab">Fermés <span
class="label label-default"><?= $dataProviderTicketClosed->totalCount ?></span></a></li>
<li class="active"><a href="#tab_1-1" data-toggle="tab">Ouverts <span
class="label label-default"><?= $dataProviderTicketOpen->totalCount ?></span></a></li>
<li class="pull-left header"><i class="fa fa-comments"></i> Tickets</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab_1-1">
<?= ticketList($context, $searchTicket, $dataProviderTicketOpen, $userCurrent); ?>
</div>
<div class="tab-pane" id="tab_2-2">
<?= ticketList($context, $searchTicket, $dataProviderTicketClosed, $userCurrent); ?>
</div>
</div>
</div>

</div>

<?php

function ticketList($context, $searchTicket, $dataProviderTicket, $userCurrent)
{
$ticketManager = TicketManager::getInstance();

$columnCreatedAt = [
'attribute' => 'created_at',
'headerOptions' => ['class' => 'td-created-at column-hide-on-mobile'],
'value' => function ($ticket) {
return date('d/m/Y', strtotime($ticket->created_at));
}
];
$columnSubject = [
'attribute' => 'subject',
'headerOptions' => ['class' => 'td-subject'],
'format' => 'raw',
'value' => function ($ticket) use ($ticketManager, $userCurrent) {

if($ticketManager->isTicketUnread($ticket, $userCurrent)) {
$firstTicketMessageUnread = $ticketManager->getFirstTicketMessageUnread($ticket, $userCurrent);
$link = '<strong>'.Html::a($ticket->subject, ['view', 'id' => $ticket->id, '#' => $firstTicketMessageUnread->id]).'</strong>';
}
else {
$link = Html::a($ticket->subject, ['view', 'id' => $ticket->id]);
}

return $link;
}
];
$columnLastMessage = [
'label' => 'Dernier message',
'headerOptions' => ['class' => 'td-last-message column-hide-on-mobile'],
'value' => function ($ticket) {
$ticketMessages = $ticket->ticketMessages;
if ($ticketMessages && is_array($ticketMessages)) {
$lastTicketMessage = end($ticketMessages);
$origin = date_create(date('Y-m-d', strtotime($lastTicketMessage->created_at)));
$target = date_create();
$interval = date_diff($origin, $target);
$days = (int)$interval->format('%R%a');

if ($days == 0) {
return "Aujourd'hui";
} elseif ($days == 1) {
return "Hier";
} else {
return $days . ' jours';
}
}

return '';
}
];
$columnMessages = [
'label' => 'Messages',
'headerOptions' => ['class' => 'td-messages column-hide-on-mobile'],
'value' => function ($ticket) {
return count($ticket->ticketMessages);
}
];
$columnButtonActions = [
'class' => 'yii\grid\ActionColumn',
'template' => '{view} {close-open}',
'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'],
'buttons' => [
'view' => function ($url, $ticket) {
$url = ['view', 'id' => $ticket->id];
return Html::a('<span class="glyphicon glyphicon-eye-open"></span>', $url, [
'title' => 'Voir le ticket', 'class' => 'btn btn-default'
]);
},
'close-open' => function ($url, $ticket) use ($ticketManager) {
if ($ticketManager->isTicketOpen($ticket)) {
$title = 'Fermer';
$url = ['close', 'id' => $ticket->id];
$glyphicon = 'glyphicon-folder-close';
} else {
$title = 'Ré-ouvrir';
$url = ['open', 'id' => $ticket->id];
$glyphicon = 'glyphicon-folder-open';
}
return Html::a('<span class="glyphicon ' . $glyphicon . '"></span>', $url, [
'title' => $title, 'class' => 'btn btn-default'
]);
}
],
];
$columnProducer = [
'attribute' => 'id_producer',
'headerOptions' => ['class' => 'td-producer'],
'value' => function ($ticket) {
return $ticket->producer->name;
}
];

if($context == 'producer') {
$columns = [
$columnCreatedAt,
$columnSubject,
$columnLastMessage,
$columnMessages,
$columnButtonActions
];
}
elseif($context == 'admin') {
$columns = [
$columnCreatedAt,
$columnProducer,
$columnSubject,
$columnLastMessage,
$columnMessages,
$columnButtonActions
];
}


$optionsGridView = [
'summary' => '',
'filterModel' => $searchTicket,
'dataProvider' => $dataProviderTicket,
'columns' => $columns,
];

return GridView::widget($optionsGridView);
}

?>

+ 15
- 6
backend/views/support/view.php 파일 보기

@@ -8,8 +8,7 @@ use yii\widgets\ActiveForm;
$ticketManager = TicketManager::getInstance();
$userManager = UserManager::getInstance();
$this->setTitle('Voir un ticket');

$this->addBreadcrumb(['label' => 'Support', 'url' => ['support/index']]);
$this->addBreadcrumb(['label' => 'Support', 'url' => ['index']]);
$this->addBreadcrumb('Voir un ticket');

?>
@@ -45,7 +44,7 @@ $this->addBreadcrumb('Voir un ticket');
<i class="fa fa-user <?= $userManager->isAdmin($ticketMessage->user) ? 'bg-orange' : 'bg-aqua'; ?>"></i>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> <?= date('d/m/Y à H:i', strtotime($ticketMessage->created_at)) ?></span>
<h3 class="timeline-header">Guillaume Bourgeois</h3>
<h3 class="timeline-header"><?= Html::encode($userManager->getUsername($ticketMessage->user)); ?></h3>
<div class="timeline-body">
<?= nl2br($ticketMessage->message); ?>
</div>
@@ -68,12 +67,22 @@ $this->addBreadcrumb('Voir un ticket');
</div>
</div>

<div class="box box-danger">
<div class="box <?= $ticketManager->isTicketOpen($ticket) ? 'box-danger' : 'box-success'; ?>">
<div class="box-header">
<h3 class="box-title"><i class="fa fa-folder"></i> Cliquez ici si vous souhaitez fermer le ticket</h3>
<h3 class="box-title">
<?php if($ticketManager->isTicketOpen($ticket)): ?>
<i class="fa fa-folder"></i> Cliquez ici si vous souhaitez fermer le ticket
<?php else: ?>
<i class="fa fa-folder-open"></i> Cliquez ici si vous souhaitez rouvrir le ticket
<?php endif; ?>
</h3>
</div>
<div class="box-body">
<?= Html::a('Fermer le ticket', ['support/close', 'id' => $ticket->id], ['class' => 'btn btn-danger btn-sm']) ?>
<?php if($ticketManager->isTicketOpen($ticket)): ?>
<?= Html::a('Fermer le ticket', ['close', 'id' => $ticket->id], ['class' => 'btn btn-danger btn-sm']) ?>
<?php else: ?>
<?= Html::a('Rouvrir le ticket', ['open', 'id' => $ticket->id], ['class' => 'btn btn-success btn-sm']) ?>
<?php endif; ?>
</div>
</div>


+ 22
- 0
backend/web/css/screen.css 파일 보기

@@ -2606,6 +2606,28 @@ termes.
padding-left: 25px;
}

/* line 6, ../sass/support/_index.scss */
.support-index .ticket-list .nav-tabs .label {
position: relative;
top: -2px;
left: 2px;
padding: 0.3em 0.6em 0.2em 0.6em;
}
/* line 15, ../sass/support/_index.scss */
.support-index .ticket-list .table .filters {
display: none;
}
/* line 19, ../sass/support/_index.scss */
.support-index .ticket-list .table .td-created-at,
.support-index .ticket-list .table .td-last-message,
.support-index .ticket-list .table .td-messages {
width: 100px;
}
/* line 24, ../sass/support/_index.scss */
.support-index .ticket-list .table .td-producer {
width: 200px;
}

/* line 5, ../sass/support/_view.scss */
.ticket-view .table tr:first-child td {
border-top: 0px none;

+ 25
- 0
backend/web/sass/support/_index.scss 파일 보기

@@ -1,4 +1,29 @@

.support-index {
.ticket-list {

.nav-tabs {
.label {
position: relative;
top: -2px;
left: 2px;
padding: 0.3em 0.6em 0.2em 0.6em;
}
}

.table {
.filters {
display: none;
}

.td-created-at,
.td-last-message,
.td-messages {
width: 100px;
}
.td-producer {
width: 200px;
}
}
}
}

+ 7
- 0
common/components/View.php 파일 보기

@@ -38,6 +38,8 @@ termes.

namespace common\components ;

use common\logic\User\User\Model\User;

class View extends \yii\web\View
{
use BusinessLogicTrait;
@@ -114,4 +116,9 @@ class View extends \yii\web\View
{
return \Yii::$app->urlManagerBackend;
}

public function getUserCurrent(): ?User
{
return \Yii::$app->user->identity;
}
}

+ 1
- 0
common/config/main.php 파일 보기

@@ -124,4 +124,5 @@ return [
}
],
'language' => 'fr-FR',
'timeZone' => 'Europe/Paris',
];

+ 25
- 7
common/logic/AbstractRepository.php 파일 보기

@@ -18,6 +18,9 @@ abstract class AbstractRepository extends AbstractService implements RepositoryI
{
$this->query->createQuery();

$this->defaultWith();
$this->defaultJoinWith();

return $this->query;
}

@@ -25,28 +28,43 @@ abstract class AbstractRepository extends AbstractService implements RepositoryI
{
$this->createQuery();

$defaultOptions = $this->getDefaultOptionsSearch();
$this->defaultWith();
$this->defaultJoinWith();
$this->defaultFilterProducerContext();
$this->defaultOrderBy();

return $this->query;
}

// with
public function defaultWith(): void
{
$defaultOptions = $this->getDefaultOptionsSearch();
if (is_array($defaultOptions['with']) && count($defaultOptions['with'])) {
$this->query->with($defaultOptions['with']);
}
}

// join with
public function defaultJoinWith(): void
{
$defaultOptions = $this->getDefaultOptionsSearch();
if (is_array($defaultOptions['join_with']) && count($defaultOptions['join_with'])) {
$this->query->joinWith($defaultOptions['join_with']);
}
}

// id producer contexte
public function defaultFilterProducerContext(): void
{
$defaultOptions = $this->getDefaultOptionsSearch();
if(isset($defaultOptions['attribute_id_producer']) && $defaultOptions['attribute_id_producer']) {
$this->query->andWhere([$defaultOptions['attribute_id_producer'] => $this->getProducerContextId()]);
}
}

// order by
public function defaultOrderBy(): void
{
$defaultOptions = $this->getDefaultOptionsSearch();
if(isset($defaultOptions['orderby']) && $defaultOptions['orderby']) {
$this->query->orderBy($defaultOptions['orderby']);
}

return $this->query;
}
}

+ 8
- 1
common/logic/Ticket/Ticket/Model/Ticket.php 파일 보기

@@ -41,6 +41,7 @@ namespace common\logic\Ticket\Ticket\Model;
use common\components\ActiveRecordCommon;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Ticket\TicketMessage\Model\TicketMessage;
use common\logic\Ticket\TicketUser\Model\TicketUser;
use common\logic\User\User\Model\User;

class Ticket extends ActiveRecordCommon
@@ -103,7 +104,13 @@ class Ticket extends ActiveRecordCommon

public function getTicketMessages()
{
return $this->hasMany(TicketMessage::class, ['id_ticket' => 'id']);
return $this->hasMany(TicketMessage::class, ['id_ticket' => 'id'])
->orderBy(['created_at' => SORT_ASC]);
}

public function getTicketUsers()
{
return $this->hasMany(TicketUser::class, ['id_ticket' => 'id']);
}

public function populateProducer(Producer $producer): void

+ 12
- 8
common/logic/Ticket/Ticket/Model/TicketSearch.php 파일 보기

@@ -53,19 +53,27 @@ class TicketSearch extends Ticket
];
}

public function search($params)
public function search($context, $params)
{
$ticketRepository = TicketRepository::getInstance();
$optionsSearch = $ticketRepository->getDefaultOptionsSearch();

$query = Ticket::find()
->with($optionsSearch['with'])
->innerJoinWith($optionsSearch['join_with'], true)
->where(['ticket.id_producer' => GlobalParam::getCurrentProducerId()]);
->innerJoinWith($optionsSearch['join_with'], true);

if($context == 'producer') {
$query->where(['ticket.id_producer' => GlobalParam::getCurrentProducerId()]);
}

$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['attributes' => ['updated_at']],
'sort' => [
'attributes' => ['updated_at'],
'defaultOrder' => [
'updated_at' => SORT_DESC,
]
],
'pagination' => [
'pageSize' => 20,
],
@@ -73,10 +81,6 @@ class TicketSearch extends Ticket

$this->load($params);

if(!$this->status) {
$this->status = Ticket::STATUS_OPEN;
}

if (!$this->validate()) {
return $dataProvider;
}

+ 20
- 3
common/logic/Ticket/Ticket/Repository/TicketRepository.php 파일 보기

@@ -3,14 +3,19 @@
namespace common\logic\Ticket\Ticket\Repository;

use common\logic\AbstractRepository;
use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\Ticket\Service\TicketSolver;
use common\logic\User\User\Model\User;

class TicketRepository extends AbstractRepository
{
protected TicketRepositoryQuery $query;
protected TicketSolver $ticketSolver;

public function loadDependencies(): void
{
$this->loadQuery(TicketRepositoryQuery::class);
$this->ticketSolver = $this->loadService(TicketSolver::class);
}

/**
@@ -19,17 +24,29 @@ class TicketRepository extends AbstractRepository
public function getDefaultOptionsSearch(): array
{
return [
self::WITH => ['user', 'producer', 'ticketMessages'],
self::WITH => ['user', 'producer', 'ticketMessages', 'ticketUsers'],
self::JOIN_WITH => [],
self::ORDER_BY => '',
self::ATTRIBUTE_ID_PRODUCER => ''
self::ATTRIBUTE_ID_PRODUCER => 'ticket.id_producer'
];
}

public function findOneTicketById(int $id)
public function findOneTicketById(int $id): ?Ticket
{
return $this->createQuery()
->filterById($id)
->findOne();
}

public function countTicketsUnreadByUser(User $user): int
{
$ticketsArray = $this->createDefaultQuery()->find();
return $this->ticketSolver->countTicketsUnreadByUserFromArray($ticketsArray, $user);
}

public function countTicketsAdminUnreadByUser(User $user): int
{
$ticketsArray = $this->createQuery()->find();
return $this->ticketSolver->countTicketsUnreadByUserFromArray($ticketsArray, $user);
}
}

+ 32
- 0
common/logic/Ticket/Ticket/Service/TicketBuilder.php 파일 보기

@@ -5,10 +5,22 @@ namespace common\logic\Ticket\Ticket\Service;
use common\logic\AbstractBuilder;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\TicketMessage\Model\TicketMessage;
use common\logic\Ticket\TicketMessage\Service\TicketMessageBuilder;
use common\logic\Ticket\TicketUser\Service\TicketUserBuilder;
use common\logic\User\User\Model\User;

class TicketBuilder extends AbstractBuilder
{
protected TicketMessageBuilder $ticketMessageBuilder;
protected TicketUserBuilder $ticketUserBuilder;

public function loadDependencies(): void
{
$this->ticketMessageBuilder = $this->loadService(TicketMessageBuilder::class);
$this->ticketUserBuilder = $this->loadService(TicketUserBuilder::class);
}

public function instanciateTicket(Producer $producer, User $user): Ticket
{
$ticket = new Ticket();
@@ -32,6 +44,8 @@ class TicketBuilder extends AbstractBuilder
{
$ticket->status = $status;
$this->saveUpdate($ticket);

$this->updateTicketUpdatedAt($ticket);
}

public function closeTicket(Ticket $ticket)
@@ -43,4 +57,22 @@ class TicketBuilder extends AbstractBuilder
{
$this->updateTicketStatus($ticket, Ticket::STATUS_OPEN);
}

public function updateTicketUpdatedAt(Ticket $ticket)
{
$ticket->updated_at = date('Y-m-d H:i:s');
$this->saveUpdate($ticket);
}

public function createTicketMessage(TicketMessage $ticketMessage)
{
$this->updateTicketUpdatedAt($ticketMessage->ticket);
return $this->saveCreate($ticketMessage);
}

public function viewTicket(Ticket $ticket, User $user)
{
$ticketUser = $this->ticketUserBuilder->createTicketUserIfNotExist($ticket, $user);
$this->ticketUserBuilder->updateTicketUserReadAt($ticketUser);
}
}

+ 64
- 2
common/logic/Ticket/Ticket/Service/TicketSolver.php 파일 보기

@@ -4,6 +4,8 @@ namespace common\logic\Ticket\Ticket\Service;

use common\logic\AbstractSolver;
use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\TicketMessage\Model\TicketMessage;
use common\logic\Ticket\TicketUser\Model\TicketUser;
use common\logic\User\User\Model\User;
use common\logic\User\User\Service\UserSolver;

@@ -41,11 +43,71 @@ class TicketSolver extends AbstractSolver
{
$classLabel = 'label-success';
$statusLabel = 'Ouvert';
if($this->isTicketClosed($ticket)) {
if ($this->isTicketClosed($ticket)) {
$classLabel = 'label-danger';
$statusLabel = 'Fermé';
}

return '<span class="label '.$classLabel.'">'.$statusLabel.'</span>';
return '<span class="label ' . $classLabel . '">' . $statusLabel . '</span>';
}

public function getTicketUser(Ticket $ticket, User $user)
{
foreach($ticket->ticketUsers as $ticketUser) {
if($ticketUser->id_user == $user->id) {
return $ticketUser;
}
}

return null;
}

public function isTicketMessageUnread(TicketMessage $ticketMessage, TicketUser $ticketUser)
{
if($ticketMessage->created_at > $ticketUser->read_at) {
return true;
}

return false;
}

public function isTicketUnread(Ticket $ticket, User $user): int
{
$ticketUser = $this->getTicketUser($ticket, $user);
if($ticketUser) {
foreach($ticket->ticketMessages as $ticketMessage) {
if($this->isTicketMessageUnread($ticketMessage, $ticketUser)) {
return true;
}
}
}

return false;
}

public function getFirstTicketMessageUnread(Ticket $ticket, User $user): ?TicketMessage
{
$ticketUser = $this->getTicketUser($ticket, $user);
if($ticketUser) {
foreach($ticket->ticketMessages as $ticketMessage) {
if($this->isTicketMessageUnread($ticketMessage, $ticketUser)) {
return $ticketMessage;
}
}
}

return null;
}

public function countTicketsUnreadByUserFromArray(array $ticketsArray, User $user): int
{
$count = 0;
foreach($ticketsArray as $ticket) {
if($this->isTicketUnread($ticket, $user)) {
$count++;
}
}

return $count;
}
}

+ 12
- 1
common/logic/Ticket/TicketUser/Repository/TicketUserRepository.php 파일 보기

@@ -3,6 +3,9 @@
namespace common\logic\Ticket\TicketUser\Repository;

use common\logic\AbstractRepository;
use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\TicketUser\Model\TicketUser;
use common\logic\User\User\Model\User;

class TicketUserRepository extends AbstractRepository
{
@@ -19,10 +22,18 @@ class TicketUserRepository extends AbstractRepository
public function getDefaultOptionsSearch(): array
{
return [
self::WITH => ['user', 'producer', 'ticketMessages'],
self::WITH => ['ticket', 'user'],
self::JOIN_WITH => [],
self::ORDER_BY => '',
self::ATTRIBUTE_ID_PRODUCER => ''
];
}

public function findOneTicketUser(Ticket $ticket, User $user): ?TicketUser
{
return $this->createDefaultQuery()
->filterByTicket($ticket)
->filterByUser($user)
->findOne();
}
}

+ 14
- 0
common/logic/Ticket/TicketUser/Repository/TicketUserRepositoryQuery.php 파일 보기

@@ -3,7 +3,9 @@
namespace common\logic\Ticket\TicketUser\Repository;

use common\logic\AbstractRepositoryQuery;
use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\TicketUser\Service\TicketUserDefinition;
use common\logic\User\User\Model\User;

class TicketUserRepositoryQuery extends AbstractRepositoryQuery
{
@@ -13,4 +15,16 @@ class TicketUserRepositoryQuery extends AbstractRepositoryQuery
{
$this->loadDefinition(TicketUserDefinition::class);
}

public function filterByTicket(Ticket $ticket): self
{
$this->andWhere(['ticket_user.id_ticket' => $ticket->id]);
return $this;
}

public function filterByUser(User $user): self
{
$this->andWhere(['ticket_user.id_user' => $user->id]);
return $this;
}
}

+ 27
- 3
common/logic/Ticket/TicketUser/Service/TicketUserBuilder.php 파일 보기

@@ -5,21 +5,45 @@ namespace common\logic\Ticket\TicketUser\Service;
use common\logic\AbstractBuilder;
use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Ticket\TicketUser\Model\TicketUser;
use common\logic\Ticket\TicketUser\Repository\TicketUserRepository;
use common\logic\User\User\Model\User;

class TicketUserBuilder extends AbstractBuilder
{
public function instanciateTicketUser(): TicketUser
protected TicketUserRepository $ticketUserRepository;

public function loadDependencies(): void
{
$this->ticketUserRepository = $this->loadService(TicketUserRepository::class);
}

public function instanciateTicketUser(Ticket $ticket, User $user): TicketUser
{
$ticketUser = new TicketUser();

$ticketUser->populateTicket($ticket);
$ticketUser->populateUser($user);

return $ticketUser;
}

public function createTicketUser(): Ticket
public function createTicketUser(Ticket $ticket, User $user): TicketUser
{
$ticketUser = $this->instanciateTicketUser();
$ticketUser = $this->instanciateTicketUser($ticket, $user);
$this->saveCreate($ticketUser);

return $ticketUser;
}

public function createTicketUserIfNotExist(Ticket $ticket, User $user): TicketUser
{
return $this->ticketUserRepository->findOneTicketUser($ticket, $user)
?? $this->createTicketUser($ticket, $user);
}

public function updateTicketUserReadAt(TicketUser $ticketUser)
{
$ticketUser->read_at = date('Y-m-d H:i:s');
$this->saveUpdate($ticketUser);
}
}

Loading…
취소
저장