diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index b694f3c..be8c522 100644 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -60,20 +60,5 @@ ] } ] - }, - { - "id": 1008503001, - "pid": "/", - "type": 2, - "subtype": 1, - "name": "QualityReport", - "uri": "{/base}/private/qualitymanagement/dashboard", - "target": "self", - "icon": null, - "order": 1, - "from": "QualityManagement", - "permission": { "permission": 2, "type": 2, "element": null }, - "parent": 1003401001, - "children": [] } ] diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index 8c5e9d5..f778336 100644 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -78,29 +78,4 @@ return [ ], ], ], - - '^/private/qualitymanagement/dashboard(\?.*$|$)' => [ - [ - 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewPrivateReportDashboard', - 'verb' => RouteVerb::GET, - 'active' => true, - 'permission' => [ - 'module' => BackendController::NAME, - 'type' => PermissionType::READ, - 'state' => PermissionCategory::PRIVATE_DASHBOARD, - ], - ], - ], - '^/private/qualitymanagement/report(\?.*$|$)' => [ - [ - 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewPrivateReport', - 'verb' => RouteVerb::GET, - 'active' => true, - 'permission' => [ - 'module' => BackendController::NAME, - 'type' => PermissionType::READ, - 'state' => PermissionCategory::PRIVATE_DASHBOARD, - ], - ], - ], ]; diff --git a/Controller/ApiController.php b/Controller/ApiController.php index d0594ad..09c0178 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace Modules\QualityManagement\Controller; +use Modules\Notification\Models\NotificationType; use Modules\QualityManagement\Models\Report; use Modules\QualityManagement\Models\ReportMapper; use Modules\Tasks\Models\TaskElementMapper; @@ -80,6 +81,11 @@ final class ApiController extends Controller $report = $this->createReportFromRequest($request); $this->createModel($request->header->account, $report, ReportMapper::class, 'report', $request->getOrigin()); + $first = \reset($report->task->taskElements); + if ($first !== false) { + $this->app->moduleManager->get('Tasks', 'Api')->createNotifications($first, NotificationType::CREATE, $request); + } + $this->createStandardCreateResponse($request, $response, $report); } @@ -95,7 +101,7 @@ final class ApiController extends Controller private function createReportFromRequest(RequestAbstract $request) : Report { $request->setData('redirect', 'qualitymanagement/report/view?for={$id}'); - $task = $this->app->moduleManager->get('Tasks')->createTaskFromRequest($request); + $task = $this->app->moduleManager->get('Tasks', 'Api')->createTaskFromRequest($request); $task->type = TaskType::HIDDEN; $task->unit ??= $this->app->unitId; diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 30f2781..f71799f 100644 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -14,10 +14,21 @@ declare(strict_types=1); namespace Modules\QualityManagement\Controller; +use Modules\Media\Models\MediaMapper; +use Modules\QualityManagement\Models\ReportMapper; +use Modules\QualityManagement\Views\ReportView; +use Modules\Tasks\Models\AccountRelationMapper; +use Modules\Tasks\Models\TaskElementMapper; +use Modules\Tasks\Models\TaskMapper; +use Modules\Tasks\Models\TaskStatus; +use Modules\Tasks\Models\TaskType; use phpOMS\Contract\RenderableInterface; +use phpOMS\DataStorage\Database\Query\Builder; +use phpOMS\DataStorage\Database\Query\OrderType; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Views\View; +use Modules\Profile\Models\SettingsEnum as ProfileSettingsEnum; /** * QualityManagement controller class. @@ -41,11 +52,48 @@ final class BackendController extends Controller * @since 1.0.0 * @codeCoverageIgnore */ - public function viewPrivateReportDashboard(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface + public function viewReportDashboard(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface { $view = new View($this->app->l11nManager, $request, $response); - $view->setTemplate('/Modules/QualityManagement/Theme/Backend/report-list'); - $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1002901101, $request, $response); + $view->setTemplate('/Modules/QualityManagement/Theme/Backend/report-dashboard'); + $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1008502001, $request, $response); + + $view->data['reports'] = ReportMapper::getAnyRelatedToUser($request->header->account) + ->with('task') + ->with('task/createdBy') + ->with('task/for') + ->with('task/taskElements') + ->with('task/taskElements/accRelation') + ->with('task/taskElements/accRelation/relation') + ->with('app') + ->sort('task/createdAt', OrderType::DESC) + ->limit(50) + ->paginate( + 'id', + $request->getData('ptype'), + $request->getDataInt('offset') + ) + ->executeGetArray(); + + $openQuery = new Builder($this->app->dbPool->get(), true); + $openQuery->innerJoin(TaskMapper::TABLE, TaskMapper::TABLE . '_d2_task') + ->on(ReportMapper::TABLE . '_d1.qualitymgmt_report_task', '=', TaskMapper::TABLE . '_d2_task.task_id') + ->innerJoin(TaskElementMapper::TABLE) + ->on(TaskMapper::TABLE . '_d2_task.' . TaskMapper::PRIMARYFIELD, '=', TaskElementMapper::TABLE . '.task_element_task') + ->innerJoin(AccountRelationMapper::TABLE) + ->on(TaskElementMapper::TABLE . '.' . TaskElementMapper::PRIMARYFIELD, '=', AccountRelationMapper::TABLE . '.task_account_task_element') + ->andWhere(AccountRelationMapper::TABLE . '.task_account_account', '=', $request->header->account); + + $view->data['open'] = ReportMapper::getAll() + ->with('task') + ->with('task/createdBy') + ->where('task/type', TaskType::TEMPLATE, '!=') + ->where('task/status', TaskStatus::OPEN) + ->sort('task/createdAt', OrderType::DESC) + ->query($openQuery) + ->executeGetArray(); + + $view->data['stats'] = ReportMapper::getStatOverview(); return $view; } @@ -62,15 +110,59 @@ final class BackendController extends Controller * @since 1.0.0 * @codeCoverageIgnore */ - public function viewReportDashboard(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface + public function viewQualityReportCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface { $view = new View($this->app->l11nManager, $request, $response); - $view->setTemplate('/Modules/QualityManagement/Theme/Backend/report-dashboard'); + $view->setTemplate('/Modules/QualityManagement/Theme/Backend/report-create'); $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1008502001, $request, $response); - $view->data['reports'] = []; - $view->data['open'] = []; - $view->data['stats'] = []; + return $view; + } + + /** + * Routing end-point for application behavior. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewQualityReport(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface + { + $view = new ReportView($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/QualityManagement/Theme/Backend/report-view'); + $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1008502001, $request, $response); + + $view->data['report'] = ReportMapper::get() + ->with('task') + ->with('task/createdBy') + ->with('task/tags') + ->with('task/tags/title') + ->with('task/taskElements') + ->with('task/taskElements/createdBy') + ->with('task/taskElements/media') + ->with('task/attributes') + ->with('task/for') + ->where('id', (int) $request->getData('id')) + ->where('task/tags/title/language', $request->header->l11n->language) + ->execute(); + + /** @var \Model\Setting $profileImage */ + $profileImage = $this->app->appSettings->get(names: ProfileSettingsEnum::DEFAULT_PROFILE_IMAGE, module: 'Profile'); + + /** @var \Modules\Media\Models\Media $image */ + $image = MediaMapper::get()->where('id', (int) $profileImage->content)->execute(); + $view->defaultProfileImage = $image; + + $accGrpSelector = new \Modules\Profile\Theme\Backend\Components\AccountGroupSelector\BaseView($this->app->l11nManager, $request, $response); + $view->data['accGrpSelector'] = $accGrpSelector; + + $editor = new \Modules\Editor\Theme\Backend\Components\Editor\BaseView($this->app->l11nManager, $request, $response); + $view->data['editor'] = $editor; return $view; } diff --git a/Models/NullReport.php b/Models/NullReport.php new file mode 100644 index 0000000..cff9e0a --- /dev/null +++ b/Models/NullReport.php @@ -0,0 +1,47 @@ +id = $id; + parent::__construct(); + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return ['id' => $this->id]; + } +} diff --git a/Theme/Backend/Lang/de.lang.php b/Theme/Backend/Lang/de.lang.php index 719d0c6..af4064a 100644 --- a/Theme/Backend/Lang/de.lang.php +++ b/Theme/Backend/Lang/de.lang.php @@ -32,6 +32,7 @@ return ['Support' => [ 'Forwarded' => 'Weitergeleitet', 'From' => 'Von', 'Group' => 'Gruppe', + 'Reference' => 'Referenz', 'InTime' => 'Rechtzeitig', 'Interval' => 'Intervall', 'Media' => 'Medien', @@ -58,6 +59,8 @@ return ['Support' => [ 'Title' => 'Titel', 'To' => 'Zu', 'Item' => 'Artikel', + 'Bill' => 'Beleg', + 'LotSN' => 'Charge/SN', 'Today' => 'Heute', 'Topic' => 'Thema', 'Type' => 'Typ', diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index f9d671d..d7cfdec 100644 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -29,6 +29,7 @@ return ['Support' => [ 'Due' => 'Due', 'Files' => 'Files', 'For' => 'For', + 'Reference' => 'Reference', 'Forwarded' => 'Forwarded', 'From' => 'From', 'Group' => 'Group', @@ -58,6 +59,8 @@ return ['Support' => [ 'Title' => 'Title', 'To' => 'To', 'Item' => 'Item', + 'Bill' => 'Bill', + 'LotSN' => 'Lot/SN', 'Today' => 'Today', 'Topic' => 'Topic', 'Type' => 'Type', diff --git a/Theme/Backend/report-create.tpl.php b/Theme/Backend/report-create.tpl.php new file mode 100644 index 0000000..d725949 --- /dev/null +++ b/Theme/Backend/report-create.tpl.php @@ -0,0 +1,66 @@ +data['nav']->render(); ?> +
+
+
+
+
getHtml('Report'); ?>
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/Theme/Backend/report-view.tpl.php b/Theme/Backend/report-view.tpl.php new file mode 100644 index 0000000..27c0219 --- /dev/null +++ b/Theme/Backend/report-view.tpl.php @@ -0,0 +1,426 @@ +data['report'] ?? new NullReport(); +$task = $report->task; +$taskMedia = $task->files; +$elements = $report->task->invertTaskElements(); +$cElements = \count($elements); +$color = 'red'; //$this->getStatus($task->status); +$isNew = $report->id === 0; + +echo $this->data['nav']->render(); ?> + +
+
+
+
getHtml('Reference'); ?>
+ +
+
+ + > +
+ +
+ + > +
+ +
+ + > +
+
+
+ +
+ isEditable) : ?> + + + +
+ + <?= $this->getHtml('User', '0', '0'); ?> + + + printHtml($task->createdBy->name1); ?> - printHtml($task->createdAt->format('Y/m/d H:i')); ?> + + + + getHtml('S' . $task->status, 'Tasks'); ?> + + +
+
+ printHtml($task->title); ?> +
description; ?>
+
+
+
+
+ +
+ + name; ?> + +
+ + +
+ priority === TaskPriority::NONE) : ?> + getHtml('Due'); ?>: printHtml($task->due->format('Y/m/d H:i')); ?> + + getHtml('Priority'); ?>: getHtml('P' . $task->priority, 'Tasks'); ?> + + +
+ tags as $tag) : ?> + + icon) ? '' : '' . $this->printHtml($tag->icon) . ''; ?> + printHtml($tag->getL11n()); ?> + + +
+
+
+
+ isEditable && $this->request->header->account === $task->createdBy->id) : ?> +
+ + + +
+ +
+
+
+
+ +
+ + isEditable) : ?> + + + $element) : ++$c; ?> + status !== TaskStatus::OPEN) + || ($previous !== null && $element->status !== $previous->status) + ) : ?> +
+
+ getHtml('status_change', 'Tasks', 'Backend'), + '' . $this->printHtml($element->createdBy->name1) . '', + $element->createdAt->format('Y-m-d H:i') + ); ?> + + getHtml('S' . $element->status, 'Tasks'); ?> + +
+
+ + + priority !== $task->priority) + || ($previous !== null && $element->priority !== $previous->priority) + ) : ?> +
+
+ getHtml('priority_change', 'Tasks', 'Backend'), + '' . $this->printHtml($element->createdBy->name1) . '', + $element->createdAt->format('Y-m-d H:i') + ); ?> + + getHtml('P' . $element->priority, 'Tasks'); ?> + +
+
+ + + description !== '') : ?> +
+
+
+ + <?= $this->getHtml('User', '0', '0'); ?> + + + printHtml($element->createdBy->name1); ?> - printHtml($element->createdAt->format('Y-m-d H:i')); ?> + +
+
+ + description !== '') : ?> +
+
description; ?>
+
+ + + files; + if (!empty($elementMedia) + || ($task->isEditable + && $this->request->header->account === $element->createdBy->id) + ) : ?> +
+ +
+ + name; ?> + +
+ + + status !== TaskStatus::CANCELED + || $element->status !== TaskStatus::DONE + || $element->status !== TaskStatus::SUSPENDED + || $c != $cElements + ) : ?> +
+ priority === TaskPriority::NONE + && ($previous !== null + && $previous->due->format('Y/m/d H:i') !== $element->due->format('Y/m/d H:i') + ) + ) : ?> + getHtml('Due'); ?>: printHtml($element->due->format('Y/m/d H:i')); ?> + priority !== $element->priority) : ?> + getHtml('Priority'); ?>: getHtml('P' . $element->priority, 'Tasks'); ?> + +
+ + + isEditable + && $this->request->header->account === $element->createdBy->id + ) : ?> +
+ + + + +
+ +
+ +
+ + + getTo(); + if (\count($tos) > 1 + || (!empty($tos) && $tos[0]->getRelation()->id !== $element->createdBy->id) + ) : ?> +
+
+ printHtml($element->createdBy->name1); ?> getHtml('forwarded_to'); ?> + + + printHtml($to->getRelation()->name1); ?> + + printHtml($to->getRelation()->name); ?> + + +
+
+ + +
+
+ +
+
+
+
getHtml('Message'); ?>
+
+
+ getData('editor')->render('task-editor'); ?> +
+ +
+ getData('editor')->getData('text')->render( + 'task-editor', + 'plain', + 'taskElementCreate', + '', '', + '/content', '{/api}task?id={?id}&csrf={$CSRF}' + ); ?> +
+ +
+ + +
+ +
+ + getData('accGrpSelector')->render('iReceiver', 'to', true); ?> +
+ +
+
+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+ +
+
+
+
+
+ +
+ + +
+
+
+ + +
+
+
+
+
diff --git a/Views/ReportView.php b/Views/ReportView.php new file mode 100644 index 0000000..c849833 --- /dev/null +++ b/Views/ReportView.php @@ -0,0 +1,104 @@ +defaultProfileImage = new NullMedia(); + parent::__construct($l11n, $request, $response); + } + + /** + * Get the profile image + * + * If the profile doesn't have an image a random default image is used + * + * @param int $account Account + * + * @return string + * + * @since 1.0.0 + */ + public function getAccountImage(int $account) : string + { + /** @var \Modules\Profile\Models\Profile $profile */ + $profile = ProfileMapper::get()->with('image')->where('account', $account)->execute(); + + if ($profile->id === 0 || $profile->image->getPath() === '') { + return UriFactory::build($this->defaultProfileImage->getPath()); + } + + return UriFactory::build($profile->image->getPath()); + } + + /** + * Get task status color. + * + * @param int $status Status + * + * @return string + * + * @since 1.0.0 + */ + public function getStatus(int $status) : string + { + if ($status === TaskStatus::OPEN) { + return 'darkblue'; + } elseif ($status === TaskStatus::DONE) { + return 'green'; + } elseif ($status === TaskStatus::WORKING) { + return 'purple'; + } elseif ($status === TaskStatus::CANCELED) { + return 'red'; + } elseif ($status === TaskStatus::SUSPENDED) { + return 'yellow'; + } + + return 'black'; + } +}