Added shareable reports

This commit is contained in:
Constantin Graf
2024-11-08 13:27:51 +01:00
committed by Constantin Graf
parent 9c82efdf07
commit 0860aa9d24
26 changed files with 986 additions and 107 deletions

View File

@@ -102,6 +102,7 @@ class ProjectController extends Controller
$project->is_billable = (bool) $request->input('is_billable');
$project->billable_rate = $request->getBillableRate();
$project->client_id = $request->input('client_id');
$project->is_public = $request->getIsPublic();
if ($this->canAccessPremiumFeatures($organization) && $request->has('estimated_time')) {
$project->estimated_time = $request->getEstimatedTime();
}
@@ -127,6 +128,9 @@ class ProjectController extends Controller
if ($request->has('is_archived')) {
$project->archived_at = $request->getIsArchived() ? Carbon::now() : null;
}
if ($request->has('is_public')) {
$project->is_public = $request->boolean('is_public');
}
if ($this->canAccessPremiumFeatures($organization) && $request->has('estimated_time')) {
$project->estimated_time = $request->getEstimatedTime();
}

View File

@@ -4,9 +4,14 @@ declare(strict_types=1);
namespace App\Http\Controllers\Api\V1\Public;
use App\Enums\TimeEntryAggregationType;
use App\Http\Controllers\Api\V1\Controller;
use App\Http\Resources\V1\Report\DetailedReportResource;
use App\Http\Resources\V1\Report\DetailedWithDataReportResource;
use App\Models\Report;
use App\Models\TimeEntry;
use App\Service\Dto\ReportPropertiesDto;
use App\Service\TimeEntryAggregationService;
use App\Service\TimeEntryFilter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
@@ -22,7 +27,7 @@ class ReportController extends Controller
*
* @operationId getPublicReport
*/
public function show(Request $request): DetailedReportResource
public function show(Request $request, TimeEntryAggregationService $timeEntryAggregationService): DetailedWithDataReportResource
{
$shareSecret = $request->header('X-Api-Key');
if (! is_string($shareSecret)) {
@@ -30,6 +35,9 @@ class ReportController extends Controller
}
$report = Report::query()
->with([
'organization',
])
->where('share_secret', '=', $shareSecret)
->where('is_public', '=', true)
->where(function (Builder $builder): void {
@@ -38,7 +46,45 @@ class ReportController extends Controller
->orWhere('public_until', '>', now());
})
->firstOrFail();
/** @var ReportPropertiesDto $properties */
$properties = $report->properties;
return new DetailedReportResource($report);
$timeEntriesQuery = TimeEntry::query()
->whereBelongsTo($report->organization, 'organization');
$filter = new TimeEntryFilter($timeEntriesQuery);
$filter->addStart($properties->start);
$filter->addEnd($properties->end);
$filter->addActive($properties->active);
$filter->addBillable($properties->billable);
$filter->addMemberIdsFilter($properties->memberIds?->toArray());
$filter->addProjectIdsFilter($properties->projectIds?->toArray());
$filter->addTagIdsFilter($properties->tagIds?->toArray());
$filter->addTaskIdsFilter($properties->taskIds?->toArray());
$filter->addClientIdsFilter($properties->clientIds?->toArray());
$timeEntriesQuery = $filter->get();
$data = $timeEntryAggregationService->getAggregatedTimeEntriesWithDescriptions(
$timeEntriesQuery->clone(),
$report->properties->group,
$report->properties->subGroup,
$report->properties->timezone,
$report->properties->weekStart,
false,
$report->properties->start,
$report->properties->end,
);
$historyData = $timeEntryAggregationService->getAggregatedTimeEntriesWithDescriptions(
$timeEntriesQuery->clone(),
TimeEntryAggregationType::fromInterval($report->properties->historyGroup),
null,
$report->properties->timezone,
$report->properties->weekStart,
true,
$report->properties->start,
$report->properties->end,
);
return new DetailedWithDataReportResource($report, $data, $historyData);
}
}

View File

@@ -4,15 +4,17 @@ declare(strict_types=1);
namespace App\Http\Controllers\Api\V1;
use App\Enums\TimeEntryAggregationType;
use App\Enums\Weekday;
use App\Http\Requests\V1\Report\ReportStoreRequest;
use App\Http\Requests\V1\Report\ReportUpdateRequest;
use App\Http\Resources\V1\Report\DetailedReportResource;
use App\Http\Resources\V1\Report\ReportCollection;
use App\Http\Resources\V1\Report\ReportResource;
use App\Models\Organization;
use App\Models\Report;
use App\Service\Dto\ReportPropertiesDto;
use App\Service\ReportService;
use App\Service\TimezoneService;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\JsonResponse;
@@ -32,6 +34,8 @@ class ReportController extends Controller
/**
* Get reports
*
* @return ReportCollection<ReportResource>
*
* @throws AuthorizationException
*
* @operationId getReports
@@ -69,9 +73,10 @@ class ReportController extends Controller
*
* @operationId createReport
*/
public function store(Organization $organization, ReportStoreRequest $request): DetailedReportResource
public function store(Organization $organization, ReportStoreRequest $request, TimezoneService $timezoneService, ReportService $reportService): DetailedReportResource
{
$this->checkPermission($organization, 'reports:create');
$user = $this->user();
$report = new Report;
$report->name = $request->getName();
@@ -79,11 +84,32 @@ class ReportController extends Controller
$isPublic = $request->getIsPublic();
$report->is_public = $isPublic;
$properties = new ReportPropertiesDto;
$properties->group = TimeEntryAggregationType::from($request->input('properties.group'));
$properties->subGroup = TimeEntryAggregationType::from($request->input('properties.sub_group'));
$properties->group = $request->getPropertyGroup();
$properties->subGroup = $request->getPropertySubGroup();
$properties->historyGroup = $request->getPropertyHistoryGroup();
$properties->start = $request->getPropertyStart();
$properties->end = $request->getPropertyEnd();
$properties->active = $request->getPropertyActive();
$properties->setMemberIds($request->input('properties.member_ids', null));
$properties->billable = $request->getPropertyBillable();
$properties->setClientIds($request->input('properties.client_ids', null));
$properties->setProjectIds($request->input('properties.project_ids', null));
$properties->setTagIds($request->input('properties.tag_ids', null));
$properties->setTaskIds($request->input('properties.task_ids', null));
$properties->weekStart = $request->has('properties.week_start') ? Weekday::from($request->input('properties.week_start')) : $user->week_start;
$timezone = $user->timezone;
if ($request->has('properties.timezone')) {
if ($timezoneService->isValid($request->input('properties.timezone'))) {
$timezone = $request->input('properties.timezone');
}
if ($timezoneService->mapLegacyTimezone($request->input('properties.timezone')) !== null) {
$timezone = $timezoneService->mapLegacyTimezone($request->input('properties.timezone'));
}
}
$properties->timezone = $timezone;
$report->properties = $properties;
if ($isPublic) {
$report->share_secret = app(ReportService::class)->generateSecret();
$report->share_secret = $reportService->generateSecret();
$report->public_until = $request->getPublicUntil();
} else {
$report->share_secret = null;
@@ -102,7 +128,7 @@ class ReportController extends Controller
*
* @operationId updateReport
*/
public function update(Organization $organization, Report $report, ReportUpdateRequest $request): DetailedReportResource
public function update(Organization $organization, Report $report, ReportUpdateRequest $request, ReportService $reportService): DetailedReportResource
{
$this->checkPermission($organization, 'reports:update', $report);
@@ -116,7 +142,7 @@ class ReportController extends Controller
$isPublic = $request->getIsPublic();
$report->is_public = $isPublic;
if ($isPublic) {
$report->share_secret = app(ReportService::class)->generateSecret();
$report->share_secret = $reportService->generateSecret();
$report->public_until = $request->getPublicUntil();
} else {
$report->share_secret = null;