diff --git a/_ide_helper_models.php b/_ide_helper_models.php index 765879485..7eab4e7e1 100644 --- a/_ide_helper_models.php +++ b/_ide_helper_models.php @@ -327,8 +327,6 @@ class IdeHelperPronouns {} * @property mixed $payload * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at - * @property-read \Spatie\MediaLibrary\MediaCollections\Models\Collections\MediaCollection $media - * @property-read int|null $media_count * @method static \Illuminate\Database\Eloquent\Builder|SettingsProperty newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|SettingsProperty newQuery() * @method static \Illuminate\Database\Eloquent\Builder|SettingsProperty query() @@ -521,6 +519,8 @@ class IdeHelperTenant {} * @property-read int|null $roles_count * @property-read \Illuminate\Database\Eloquent\Collection $serviceRequestAssignments * @property-read int|null $service_request_assignments_count + * @property-read \Illuminate\Database\Eloquent\Collection $serviceRequestTypeIndividualAssignment + * @property-read int|null $service_request_type_individual_assignment_count * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions * @property-read int|null $subscriptions_count * @property-read \Illuminate\Database\Eloquent\Collection $teams @@ -1261,6 +1261,8 @@ class IdeHelperContactStatus {} * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at + * @property array|null $domains + * @property bool $is_contact_generation_enabled * @property-read \Illuminate\Database\Eloquent\Collection $audits * @property-read int|null $audits_count * @property-read \Illuminate\Database\Eloquent\Collection $contacts @@ -1280,10 +1282,12 @@ class IdeHelperContactStatus {} * @method static \Illuminate\Database\Eloquent\Builder|Organization whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereDeletedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|Organization whereDomains($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereEmail($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereFacebookUrl($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereIndustryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Organization whereIsContactGenerationEnabled($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereLinkedinUrl($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereName($value) * @method static \Illuminate\Database\Eloquent\Builder|Organization whereNumberOfEmployees($value) @@ -2158,6 +2162,7 @@ class IdeHelperKnowledgeBaseCategory {} * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at + * @property int $portal_view_count * @property-read \Illuminate\Database\Eloquent\Collection $audits * @property-read int|null $audits_count * @property-read \AidingApp\KnowledgeBase\Models\KnowledgeBaseCategory|null $category @@ -2181,6 +2186,7 @@ class IdeHelperKnowledgeBaseCategory {} * @method static \Illuminate\Database\Eloquent\Builder|KnowledgeBaseItem whereDeletedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|KnowledgeBaseItem whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|KnowledgeBaseItem whereNotes($value) + * @method static \Illuminate\Database\Eloquent\Builder|KnowledgeBaseItem wherePortalViewCount($value) * @method static \Illuminate\Database\Eloquent\Builder|KnowledgeBaseItem wherePublic($value) * @method static \Illuminate\Database\Eloquent\Builder|KnowledgeBaseItem whereQualityId($value) * @method static \Illuminate\Database\Eloquent\Builder|KnowledgeBaseItem whereStatusId($value) @@ -2963,6 +2969,7 @@ class IdeHelperServiceRequestPriority {} * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at + * @property bool $is_system_protected * @property-read \Illuminate\Database\Eloquent\Collection $audits * @property-read int|null $audits_count * @property-read \Illuminate\Database\Eloquent\Collection $serviceRequests @@ -2977,6 +2984,7 @@ class IdeHelperServiceRequestPriority {} * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestStatus whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestStatus whereDeletedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestStatus whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestStatus whereIsSystemProtected($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestStatus whereName($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestStatus whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestStatus withTrashed() @@ -3001,9 +3009,16 @@ class IdeHelperServiceRequestStatus {} * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at + * @property \AidingApp\ServiceManagement\Enums\ServiceRequestTypeAssignmentTypes $assignment_type + * @property string|null $assignment_type_individual_id + * @property-read \App\Models\User|null $assignmentTypeIndividual + * @property-read \Illuminate\Database\Eloquent\Collection $auditors + * @property-read int|null $auditors_count * @property-read \Illuminate\Database\Eloquent\Collection $audits * @property-read int|null $audits_count * @property-read \AidingApp\ServiceManagement\Models\ServiceRequestForm|null $form + * @property-read \Illuminate\Database\Eloquent\Collection $managers + * @property-read int|null $managers_count * @property-read \Illuminate\Database\Eloquent\Collection $priorities * @property-read int|null $priorities_count * @property-read \Illuminate\Database\Eloquent\Collection $serviceRequests @@ -3013,6 +3028,8 @@ class IdeHelperServiceRequestStatus {} * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType newQuery() * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType query() + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType whereAssignmentType($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType whereAssignmentTypeIndividualId($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType whereDeletedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestType whereDescription($value) @@ -3031,6 +3048,58 @@ class IdeHelperServiceRequestStatus {} class IdeHelperServiceRequestType {} } +namespace AidingApp\ServiceManagement\Models{ +/** + * AidingApp\ServiceManagement\Models\ServiceRequestTypeAuditor + * + * @property string $id + * @property string $service_request_type_id + * @property string $team_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \AidingApp\ServiceManagement\Models\ServiceRequestType $serviceRequestType + * @property-read \AidingApp\Team\Models\Team $team + * @method static \AidingApp\ServiceManagement\Database\Factories\ServiceRequestTypeAuditorFactory factory($count = null, $state = []) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor query() + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor whereServiceRequestTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor whereTeamId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeAuditor whereUpdatedAt($value) + * @mixin \Eloquent + */ + #[\AllowDynamicProperties] + class IdeHelperServiceRequestTypeAuditor {} +} + +namespace AidingApp\ServiceManagement\Models{ +/** + * AidingApp\ServiceManagement\Models\ServiceRequestTypeManager + * + * @property string $id + * @property string $service_request_type_id + * @property string $team_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \AidingApp\ServiceManagement\Models\ServiceRequestType $serviceRequestType + * @property-read \AidingApp\Team\Models\Team $team + * @method static \AidingApp\ServiceManagement\Database\Factories\ServiceRequestTypeManagerFactory factory($count = null, $state = []) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager query() + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager whereServiceRequestTypeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager whereTeamId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ServiceRequestTypeManager whereUpdatedAt($value) + * @mixin \Eloquent + */ + #[\AllowDynamicProperties] + class IdeHelperServiceRequestTypeManager {} +} + namespace AidingApp\ServiceManagement\Models{ /** * AidingApp\ServiceManagement\Models\ServiceRequestUpdate @@ -3164,7 +3233,11 @@ class IdeHelperTask {} * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $deleted_at + * @property-read \Illuminate\Database\Eloquent\Collection $auditableServiceRequestTypes + * @property-read int|null $auditable_service_request_types_count * @property-read \AidingApp\Division\Models\Division|null $division + * @property-read \Illuminate\Database\Eloquent\Collection $managableServiceRequestTypes + * @property-read int|null $managable_service_request_types_count * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read int|null $users_count * @method static \AidingApp\Team\Database\Factories\TeamFactory factory($count = null, $state = []) diff --git a/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource.php b/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource.php index 5cca410da..a97009f80 100644 --- a/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource.php +++ b/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource.php @@ -53,13 +53,13 @@ class KnowledgeBaseItemResource extends Resource protected static ?int $navigationSort = 20; - protected static ?string $breadcrumb = 'Knowledge Management'; + protected static ?string $breadcrumb = 'Knowledge Base'; protected static ?string $modelLabel = 'knowledge base article'; protected static ?string $navigationIcon = 'heroicon-o-book-open'; - protected static ?string $navigationLabel = 'Knowledge Management'; + protected static ?string $navigationLabel = 'Knowledge Base'; protected static ?string $recordTitleAttribute = 'title'; diff --git a/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource/Pages/ListKnowledgeBaseItems.php b/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource/Pages/ListKnowledgeBaseItems.php index 3d1726212..ca37e1872 100644 --- a/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource/Pages/ListKnowledgeBaseItems.php +++ b/app-modules/knowledge-base/src/Filament/Resources/KnowledgeBaseItemResource/Pages/ListKnowledgeBaseItems.php @@ -66,7 +66,7 @@ class ListKnowledgeBaseItems extends ListRecords { - protected ?string $heading = 'Knowledge Management'; + protected ?string $heading = 'Knowledge Base'; protected static string $resource = KnowledgeBaseItemResource::class; diff --git a/app-modules/service-management/database/migrations/2024_10_11_154630_add_assignment_type_columns_to_service_request_types_table.php b/app-modules/service-management/database/migrations/2024_10_11_154630_add_assignment_type_columns_to_service_request_types_table.php new file mode 100644 index 000000000..0321abd89 --- /dev/null +++ b/app-modules/service-management/database/migrations/2024_10_11_154630_add_assignment_type_columns_to_service_request_types_table.php @@ -0,0 +1,57 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +use Illuminate\Support\Facades\Schema; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Migrations\Migration; + +return new class () extends Migration { + public function up(): void + { + Schema::table('service_request_types', function (Blueprint $table) { + $table->string('assignment_type')->default('none'); + $table->foreignUuid('assignment_type_individual_id')->nullable()->constrained('users')->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('service_request_types', function (Blueprint $table) { + $table->dropColumn('assignment_type'); + $table->dropConstrainedForeignId(['assignment_type_individual_id']); + }); + } +}; diff --git a/app-modules/service-management/database/migrations/2024_10_11_163839_data_activate_feature_flag_service_request_types_assignments.php b/app-modules/service-management/database/migrations/2024_10_11_163839_data_activate_feature_flag_service_request_types_assignments.php new file mode 100644 index 000000000..468b6a045 --- /dev/null +++ b/app-modules/service-management/database/migrations/2024_10_11_163839_data_activate_feature_flag_service_request_types_assignments.php @@ -0,0 +1,50 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +use Illuminate\Database\Migrations\Migration; +use App\Features\ServiceRequestTypeAssignments; + +return new class () extends Migration { + public function up(): void + { + ServiceRequestTypeAssignments::activate(); + } + + public function down(): void + { + ServiceRequestTypeAssignments::deactivate(); + } +}; diff --git a/app-modules/service-management/resources/views/filament/forms/assignment-type-label.blade.php b/app-modules/service-management/resources/views/filament/forms/assignment-type-label.blade.php new file mode 100644 index 000000000..77e00b4df --- /dev/null +++ b/app-modules/service-management/resources/views/filament/forms/assignment-type-label.blade.php @@ -0,0 +1,34 @@ +{{-- + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +--}} +Assignment Type diff --git a/app-modules/service-management/src/Enums/ServiceRequestTypeAssignmentTypes.php b/app-modules/service-management/src/Enums/ServiceRequestTypeAssignmentTypes.php new file mode 100644 index 000000000..75857f03d --- /dev/null +++ b/app-modules/service-management/src/Enums/ServiceRequestTypeAssignmentTypes.php @@ -0,0 +1,56 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AidingApp\ServiceManagement\Enums; + +use Filament\Support\Contracts\HasLabel; + +// TODO This might belong in a more generalized space so we can re-use this across modules +enum ServiceRequestTypeAssignmentTypes: string implements HasLabel +{ + case None = 'none'; + + case Individual = 'individual'; + + case RoundRobin = 'round-robin'; + + case Workload = 'workload'; + + public function getLabel(): string + { + return str()->headline($this->name); + } +} diff --git a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php index 96772b2aa..24af61276 100644 --- a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php +++ b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php @@ -74,7 +74,14 @@ public function table(Table $table): Table 'sla', ], 'status', - ])) + ]) + ->when(! auth()->user()->hasRole('authorization.super_admin'), function (Builder $q) { + return $q->whereHas('priority.type.managers', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + })->orWhereHas('priority.type.auditors', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + }); + })) ->columns([ IdColumn::make(), TextColumn::make('service_request_number') diff --git a/app-modules/service-management/src/Filament/Resources/ServiceRequestTypeResource.php b/app-modules/service-management/src/Filament/Resources/ServiceRequestTypeResource.php index d6398ad59..6ba50992c 100644 --- a/app-modules/service-management/src/Filament/Resources/ServiceRequestTypeResource.php +++ b/app-modules/service-management/src/Filament/Resources/ServiceRequestTypeResource.php @@ -48,6 +48,7 @@ use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource\Pages\CreateServiceRequestType; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource\Pages\ManageServiceRequestTypeAuditors; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource\Pages\ManageServiceRequestTypeManagers; +use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource\Pages\EditServiceRequestTypeAssignments; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource\RelationManagers\ServiceRequestPrioritiesRelationManager; class ServiceRequestTypeResource extends Resource @@ -82,6 +83,7 @@ public static function getRecordSubNavigation(Page $page): array EditServiceRequestType::class, ManageServiceRequestTypeManagers::class, ManageServiceRequestTypeAuditors::class, + EditServiceRequestTypeAssignments::class, ]); } @@ -94,6 +96,7 @@ public static function getPages(): array 'edit' => EditServiceRequestType::route('/{record}/edit'), 'service-request-type-managers' => ManageServiceRequestTypeManagers::route('/{record}/managers'), 'service-request-type-auditors' => ManageServiceRequestTypeAuditors::route('/{record}/auditors'), + 'service-request-type-assignments' => EditServiceRequestTypeAssignments::route('/{record}/assignments'), ]; } } diff --git a/app-modules/service-management/src/Filament/Resources/ServiceRequestTypeResource/Pages/EditServiceRequestTypeAssignments.php b/app-modules/service-management/src/Filament/Resources/ServiceRequestTypeResource/Pages/EditServiceRequestTypeAssignments.php new file mode 100644 index 000000000..db220f21d --- /dev/null +++ b/app-modules/service-management/src/Filament/Resources/ServiceRequestTypeResource/Pages/EditServiceRequestTypeAssignments.php @@ -0,0 +1,125 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource\Pages; + +use Filament\Forms\Get; +use Filament\Forms\Form; +use Illuminate\Support\HtmlString; +use Filament\Forms\Components\Radio; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Section; +use Filament\Resources\Pages\EditRecord; +use App\Filament\Forms\Components\Heading; +use App\Filament\Forms\Components\Paragraph; +use App\Features\ServiceRequestTypeAssignments; +use Illuminate\Contracts\Database\Eloquent\Builder; +use AidingApp\ServiceManagement\Models\ServiceRequestType; +use AidingApp\ServiceManagement\Enums\ServiceRequestTypeAssignmentTypes; +use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource; +use AidingApp\ServiceManagement\Rules\ServiceRequestTypeAssignmentsIndividualUserMustBeAManager; + +class EditServiceRequestTypeAssignments extends EditRecord +{ + protected static string $resource = ServiceRequestTypeResource::class; + + protected static ?string $title = 'Assignments'; + + public static function canAccess(array $parameters = []): bool + { + return ServiceRequestTypeAssignments::active() && parent::canAccess($parameters); + } + + public function getRelationManagers(): array + { + // Needed to prevent Filament from loading the relation managers on this page. + return []; + } + + public function form(Form $form): Form + { + return $form + ->schema([ + Section::make() + ->columns() + ->schema([ + Heading::make() + ->content('Assignments'), + Paragraph::make() + ->content('This page is used to configure the assignment methodology for this service request type.'), + Radio::make('assignment_type') + ->live() + ->columnSpanFull() + ->label( + new HtmlString( + view('service-management::filament.forms.assignment-type-label')->render() + ) + ) + ->options(ServiceRequestTypeAssignmentTypes::class) + ->enum(ServiceRequestTypeAssignmentTypes::class) + ->required(), + Select::make('assignment_type_individual_id') + ->label('Assignment Individual') + ->columnSpanFull() + ->relationship( + name: 'assignmentTypeIndividual', + titleAttribute: 'name', + modifyQueryUsing: fn (Builder $query) => $query->whereRelation( + 'teams.managableServiceRequestTypes', + 'service_request_types.id', + $this->record->getKey(), + ) + ) + ->searchable(['name', 'email']) + ->preload() + ->required() + ->rules(fn (ServiceRequestType $record) => [new ServiceRequestTypeAssignmentsIndividualUserMustBeAManager($record)]) + ->visible(fn (Get $get) => $get('assignment_type') === ServiceRequestTypeAssignmentTypes::Individual->value), + Heading::make() + ->three() + ->content('Assignment Types'), + Paragraph::make() + ->content('None: No assignment is made when this option is selected, allowing service requests to remain unassigned until manual intervention. Ideal for flexible workflows where task assignment is determined later.'), + Paragraph::make() + ->content('Individual: All service requests are assigned to a specific manager. Best suited for cases with a dedicated resource responsible for managing tasks, ensuring consistent oversight and accountability.'), + Paragraph::make() + ->content('Round Robin: Service requests are distributed evenly among request managers in a circular order. This ensures fair ticket allocation but doesn\'t factor in current workloads.'), + Paragraph::make() + ->content('Workload: Assignments are made based on the current workload, with requests directed to the user handling the fewest open tickets. This method balances workloads, improving efficiency and avoiding bottlenecks.'), + ]), + ]); + } +} diff --git a/app-modules/service-management/src/Models/ServiceRequestType.php b/app-modules/service-management/src/Models/ServiceRequestType.php index f8893fbac..f628f5d3e 100644 --- a/app-modules/service-management/src/Models/ServiceRequestType.php +++ b/app-modules/service-management/src/Models/ServiceRequestType.php @@ -36,6 +36,7 @@ namespace AidingApp\ServiceManagement\Models; +use App\Models\User; use DateTimeInterface; use App\Models\BaseModel; use AidingApp\Team\Models\Team; @@ -45,8 +46,10 @@ use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use AidingApp\Audit\Models\Concerns\Auditable as AuditableTrait; +use AidingApp\ServiceManagement\Enums\ServiceRequestTypeAssignmentTypes; /** * @mixin IdeHelperServiceRequestType @@ -64,12 +67,14 @@ class ServiceRequestType extends BaseModel implements Auditable 'has_enabled_nps', 'description', 'icon', + 'assignment_type', ]; protected $casts = [ 'has_enabled_feedback_collection' => 'boolean', 'has_enabled_csat' => 'boolean', 'has_enabled_nps' => 'boolean', + 'assignment_type' => ServiceRequestTypeAssignmentTypes::class, ]; public function serviceRequests(): HasManyThrough @@ -101,6 +106,15 @@ public function auditors(): BelongsToMany ->withTimestamps(); } + public function assignmentTypeIndividual(): BelongsTo + { + return $this->belongsTo( + related: User::class, + foreignKey: 'assignment_type_individual_id', + relation: 'serviceRequestTypeIndividualAssignment', + ); + } + protected function serializeDate(DateTimeInterface $date): string { return $date->format(config('project.datetime_format') ?? 'Y-m-d H:i:s'); diff --git a/app-modules/service-management/src/Models/ServiceRequestTypeAuditor.php b/app-modules/service-management/src/Models/ServiceRequestTypeAuditor.php index 2611830be..001d96286 100644 --- a/app-modules/service-management/src/Models/ServiceRequestTypeAuditor.php +++ b/app-modules/service-management/src/Models/ServiceRequestTypeAuditor.php @@ -42,6 +42,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Factories\HasFactory; +/** + * @mixin IdeHelperServiceRequestTypeAuditor + */ class ServiceRequestTypeAuditor extends Pivot { use HasFactory; diff --git a/app-modules/service-management/src/Models/ServiceRequestTypeManager.php b/app-modules/service-management/src/Models/ServiceRequestTypeManager.php index 3f540ecb9..01b241aa2 100644 --- a/app-modules/service-management/src/Models/ServiceRequestTypeManager.php +++ b/app-modules/service-management/src/Models/ServiceRequestTypeManager.php @@ -42,6 +42,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Factories\HasFactory; +/** + * @mixin IdeHelperServiceRequestTypeManager + */ class ServiceRequestTypeManager extends Pivot { use HasFactory; diff --git a/app-modules/service-management/src/Policies/ServiceRequestPolicy.php b/app-modules/service-management/src/Policies/ServiceRequestPolicy.php index 0f7099c59..691ff524e 100644 --- a/app-modules/service-management/src/Policies/ServiceRequestPolicy.php +++ b/app-modules/service-management/src/Policies/ServiceRequestPolicy.php @@ -76,6 +76,18 @@ public function view(Authenticatable $authenticatable, ServiceRequest $serviceRe return Response::deny('You do not have permission to view this service request.'); } + if (! auth()->user()->hasRole('authorization.super_admin')) { + $team = auth()->user()->teams()->first(); + + if (! $serviceRequest?->priority?->type?->managers()->exists() && ! $serviceRequest?->priority?->type?->auditors()->exists()) { + return Response::deny("You don't have permission to view this service request because you're not an auditor or manager."); + } + + if (! $serviceRequest?->priority?->type?->managers->contains('id', $team?->getKey()) && ! $serviceRequest?->priority?->type?->auditors->contains('id', $team?->getKey())) { + return Response::deny("You don't have permission to view this service request because you're not an auditor or manager."); + } + } + return $authenticatable->canOrElse( abilities: ['service_request.*.view', "service_request.{$serviceRequest->id}.view"], denyResponse: 'You do not have permission to view this service request.' diff --git a/app-modules/service-management/src/Rules/ServiceRequestTypeAssignmentsIndividualUserMustBeAManager.php b/app-modules/service-management/src/Rules/ServiceRequestTypeAssignmentsIndividualUserMustBeAManager.php new file mode 100644 index 000000000..811e0bc2b --- /dev/null +++ b/app-modules/service-management/src/Rules/ServiceRequestTypeAssignmentsIndividualUserMustBeAManager.php @@ -0,0 +1,61 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AidingApp\ServiceManagement\Rules; + +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; +use Illuminate\Translation\PotentiallyTranslatedString; +use AidingApp\ServiceManagement\Models\ServiceRequestType; + +class ServiceRequestTypeAssignmentsIndividualUserMustBeAManager implements ValidationRule +{ + public function __construct( + protected ServiceRequestType $serviceRequestType + ) {} + + /** + * Run the validation rule. + * + * @param Closure(string): PotentiallyTranslatedString $fail + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + if ($this->serviceRequestType->managers()->whereRelation('users', 'users.id', $value)->doesntExist()) { + $fail('The selected user must be in a team designated as managers of this Service Request Type.'); + } + } +} diff --git a/app-modules/service-management/tests/RequestFactories/EditServiceRequestTypeAssignmentsRequestFactory.php b/app-modules/service-management/tests/RequestFactories/EditServiceRequestTypeAssignmentsRequestFactory.php new file mode 100644 index 000000000..f129705ef --- /dev/null +++ b/app-modules/service-management/tests/RequestFactories/EditServiceRequestTypeAssignmentsRequestFactory.php @@ -0,0 +1,82 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AidingApp\ServiceManagement\Tests\RequestFactories; + +use App\Models\User; +use AidingApp\Team\Models\Team; +use Worksome\RequestFactories\RequestFactory; +use AidingApp\ServiceManagement\Enums\ServiceRequestTypeAssignmentTypes; + +class EditServiceRequestTypeAssignmentsRequestFactory extends RequestFactory +{ + public function definition(): array + { + return [ + 'assignment_type' => fake()->randomElement(ServiceRequestTypeAssignmentTypes::cases())->value, + ]; + } + + public function withRandomTypeNotIncludingIndividual(): static + { + return $this->state([ + 'assignment_type' => fake()->randomElement(array_filter(ServiceRequestTypeAssignmentTypes::cases(), fn (ServiceRequestTypeAssignmentTypes $type) => $type !== ServiceRequestTypeAssignmentTypes::Individual))->value, + ]); + } + + public function withIndividualType(): static + { + return $this->state([ + 'assignment_type' => ServiceRequestTypeAssignmentTypes::Individual->value, + ]); + } + + public function withIndividualId(?Team $team = null): static + { + $userFactory = User::factory(); + + if ($team) { + $userFactory = $userFactory->hasAttached( + factory: $team, + relationship: 'teams' + ); + } + + return $this->state([ + 'assignment_type_individual_id' => $userFactory, + ]); + } +} diff --git a/app-modules/service-management/tests/ServiceRequest/ListServiceRequestsTest.php b/app-modules/service-management/tests/ServiceRequest/ListServiceRequestsTest.php index 417d89baf..5158ad2f2 100644 --- a/app-modules/service-management/tests/ServiceRequest/ListServiceRequestsTest.php +++ b/app-modules/service-management/tests/ServiceRequest/ListServiceRequestsTest.php @@ -35,6 +35,7 @@ */ use App\Models\User; +use AidingApp\Team\Models\Team; use function Tests\asSuperAdmin; @@ -46,6 +47,8 @@ use AidingApp\Contact\Models\Contact; use AidingApp\Contact\Models\Organization; use AidingApp\ServiceManagement\Models\ServiceRequest; +use AidingApp\ServiceManagement\Models\ServiceRequestType; +use AidingApp\ServiceManagement\Models\ServiceRequestPriority; use AidingApp\ServiceManagement\Models\ServiceRequestAssignment; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource\Pages\ListServiceRequests; @@ -164,3 +167,87 @@ ) ->assertCanNotSeeTableRecords($serviceRequestsNotInOrganization); }); + +test('service requests only visible to service request type managers', function () { + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $user->givePermissionTo('service_request.view-any'); + + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + + actingAs($user); + + $serviceRequests = ServiceRequest::factory() + ->count(3) + ->create(); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->managers()->attach($team); + + $serviceRequestsWithManager = ServiceRequest::factory()->state([ + 'priority_id' => ServiceRequestPriority::factory()->create([ + 'type_id' => $serviceRequestType->getKey(), + ])->getKey(), + ]) + ->count(3) + ->create(); + + livewire(ListServiceRequests::class) + ->assertCanSeeTableRecords( + $serviceRequestsWithManager + ) + ->assertCanNotSeeTableRecords($serviceRequests); +}); + +test('service requests only visible to service request type auditors', function () { + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $user->givePermissionTo('service_request.view-any'); + + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + + actingAs($user); + + $serviceRequests = ServiceRequest::factory() + ->count(3) + ->create(); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->auditors()->attach($team); + + $serviceRequestsWithAuditors = ServiceRequest::factory()->state([ + 'priority_id' => ServiceRequestPriority::factory()->create([ + 'type_id' => $serviceRequestType->getKey(), + ])->getKey(), + ]) + ->count(3) + ->create(); + + livewire(ListServiceRequests::class) + ->assertCanSeeTableRecords( + $serviceRequestsWithAuditors + ) + ->assertCanNotSeeTableRecords($serviceRequests); +}); diff --git a/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php b/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php index c7965e10a..96194f0b3 100644 --- a/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php +++ b/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php @@ -35,6 +35,7 @@ */ use App\Models\User; +use AidingApp\Team\Models\Team; use function Tests\asSuperAdmin; @@ -43,9 +44,12 @@ use function Pest\Laravel\actingAs; use function Pest\Livewire\livewire; +use AidingApp\Contact\Models\Contact; use AidingApp\Authorization\Enums\LicenseType; use AidingApp\ServiceManagement\Models\ServiceRequest; +use AidingApp\ServiceManagement\Models\ServiceRequestType; use AidingApp\ServiceManagement\Models\ServiceRequestStatus; +use AidingApp\ServiceManagement\Models\ServiceRequestPriority; use AidingApp\ServiceManagement\Enums\SystemServiceRequestClassification; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource\Pages\ManageAssignments; @@ -89,15 +93,7 @@ $serviceRequest = ServiceRequest::factory()->create(); - actingAs($user) - ->get( - ServiceRequestResource::getUrl('view', [ - 'record' => $serviceRequest, - ]) - )->assertForbidden(); - - $user->givePermissionTo('service_request.view-any'); - $user->givePermissionTo('service_request.*.view'); + asSuperAdmin($user); actingAs($user) ->get( @@ -116,12 +112,9 @@ $user = User::factory()->licensed(LicenseType::cases())->create(); - $user->givePermissionTo('service_request.view-any'); - $user->givePermissionTo('service_request.*.view'); - $serviceRequest = ServiceRequest::factory()->create(); - actingAs($user) + asSuperAdmin($user) ->get( ServiceRequestResource::getUrl('view', [ 'record' => $serviceRequest, @@ -143,17 +136,12 @@ test('service request lock icon is shown when status classification closed', function (string $pages) { $user = User::factory()->licensed(LicenseType::cases())->create(); - $user->givePermissionTo('service_request.view-any'); - $user->givePermissionTo('service_request.*.view'); - $user->givePermissionTo('service_request_assignment.view-any'); - $user->givePermissionTo('service_request_update.view-any'); - - actingAs($user); + asSuperAdmin($user); $serviceRequest = ServiceRequest::factory([ 'status_id' => ServiceRequestStatus::factory()->create([ 'classification' => SystemServiceRequestClassification::Closed, - ])->id, + ])->getKey(), ])->create(); livewire($pages, [ @@ -166,3 +154,102 @@ ManageAssignments::class, ManageServiceRequestUpdate::class, ]); + +test('service requests not authorized if user is not an auditor or manager of the service request type', function () { + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $user->givePermissionTo('service_request.view-any'); + $user->givePermissionTo('service_request.*.view'); + + $user->refresh(); + + actingAs($user); + + $serviceRequest = ServiceRequest::factory() + ->create(); + + livewire(ViewServiceRequest::class, [ + 'record' => $serviceRequest->getRouteKey(), + ]) + ->assertForbidden(); +}); + +test('view service request page visible if the user is an auditor of the service request type', function () { + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $user->givePermissionTo('service_request.view-any'); + $user->givePermissionTo('service_request.*.view'); + + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + + actingAs($user); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->auditors()->attach($team); + + $serviceRequestsWithAuditor = ServiceRequest::factory()->state([ + 'priority_id' => ServiceRequestPriority::factory()->create([ + 'type_id' => $serviceRequestType->getKey(), + ])->getKey(), + ]) + ->create(); + + livewire(ViewServiceRequest::class, [ + 'record' => $serviceRequestsWithAuditor->getRouteKey(), + ]) + ->assertSuccessful(); +}); + +test('view service request page visible if the user is a manager of the service request type', function () { + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $user->givePermissionTo('service_request.view-any'); + $user->givePermissionTo('service_request.*.view'); + + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + + actingAs($user); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->managers()->attach($team); + + $serviceRequestsWithManager = ServiceRequest::factory()->state([ + 'priority_id' => ServiceRequestPriority::factory()->create([ + 'type_id' => $serviceRequestType->getKey(), + ])->getKey(), + ]) + ->create(); + + livewire(ViewServiceRequest::class, [ + 'record' => $serviceRequestsWithManager->getRouteKey(), + ]) + ->assertSuccessful(); +}); diff --git a/app-modules/service-management/tests/ServiceRequestType/EditServiceRequestTypeAssignmentsTest.php b/app-modules/service-management/tests/ServiceRequestType/EditServiceRequestTypeAssignmentsTest.php new file mode 100644 index 000000000..403678a57 --- /dev/null +++ b/app-modules/service-management/tests/ServiceRequestType/EditServiceRequestTypeAssignmentsTest.php @@ -0,0 +1,228 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +use App\Models\User; +use AidingApp\Team\Models\Team; + +use function Tests\asSuperAdmin; + +use App\Settings\LicenseSettings; + +use function Pest\Laravel\actingAs; +use function Pest\Livewire\livewire; + +use AidingApp\Contact\Models\Contact; +use Illuminate\Validation\Rules\Enum; + +use function Pest\Laravel\assertDatabaseHas; +use function PHPUnit\Framework\assertEquals; + +use AidingApp\ServiceManagement\Models\ServiceRequestType; +use AidingApp\ServiceManagement\Enums\ServiceRequestTypeAssignmentTypes; +use AidingApp\ServiceManagement\Rules\ServiceRequestTypeAssignmentsIndividualUserMustBeAManager; +use AidingApp\ServiceManagement\Tests\RequestFactories\EditServiceRequestTypeAssignmentsRequestFactory; +use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestTypeResource\Pages\EditServiceRequestTypeAssignments; + +test('A successful action on the EditServiceRequestTypeAssignments page', function () { + $serviceRequestType = ServiceRequestType::factory()->create(); + + asSuperAdmin() + ->get( + EditServiceRequestTypeAssignments::getUrl([ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ) + ->assertSuccessful(); + + $editRequest = EditServiceRequestTypeAssignmentsRequestFactory::new()->withRandomTypeNotIncludingIndividual()->create(); + + livewire(EditServiceRequestTypeAssignments::class, [ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ->assertFormSet([ + 'assignment_type' => ServiceRequestTypeAssignmentTypes::None->value, + ]) + ->fillForm($editRequest) + ->call('save') + ->assertHasNoFormErrors(); + + assertEquals($editRequest['assignment_type'], $serviceRequestType->fresh()->assignment_type->value); +}); + +test('A successful action on the EditServiceRequestTypeAssignments page when the type selected is Individual', function () { + $managerTeam = Team::factory()->create(); + + $serviceRequestType = ServiceRequestType::factory() + ->hasAttached( + factory: $managerTeam, + relationship: 'managers' + ) + ->create(); + + asSuperAdmin() + ->get( + EditServiceRequestTypeAssignments::getUrl([ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ) + ->assertSuccessful(); + + $editRequest = EditServiceRequestTypeAssignmentsRequestFactory::new() + ->withIndividualType() + ->withIndividualId($managerTeam) + ->create(); + + livewire(EditServiceRequestTypeAssignments::class, [ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ->fillForm($editRequest) + ->call('save') + ->assertHasNoFormErrors(); + + assertEquals($editRequest['assignment_type'], $serviceRequestType->fresh()->assignment_type->value); +}); + +test('EditServiceRequestTypeAssignments requires valid data', function (EditServiceRequestTypeAssignmentsRequestFactory $data, $errors) { + asSuperAdmin(); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + livewire(EditServiceRequestTypeAssignments::class, [ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ->fillForm($data->create()) + ->call('save') + ->assertHasFormErrors($errors); + + assertDatabaseHas(ServiceRequestType::class, $serviceRequestType->toArray()); +})->with( + [ + 'assignment_type is required' => [EditServiceRequestTypeAssignmentsRequestFactory::new()->state(['assignment_type' => null]), ['assignment_type' => 'required']], + 'assignment_type is not a valid enum value' => [EditServiceRequestTypeAssignmentsRequestFactory::new()->state(['assignment_type' => 'blah']), ['assignment_type' => Enum::class]], + 'assignment_type_individual_id is required when assignment_type is Individual' => [EditServiceRequestTypeAssignmentsRequestFactory::new()->withIndividualType()->state(['assignment_type_individual_id' => null]), ['assignment_type_individual_id' => 'required']], + 'assignment_type_individual_id must be a User in the ServiceRequestTypes managers' => [EditServiceRequestTypeAssignmentsRequestFactory::new()->withIndividualType()->state(['assignment_type_individual_id' => User::factory()]), ['assignment_type_individual_id' => ServiceRequestTypeAssignmentsIndividualUserMustBeAManager::class]], + ] +); + +// Permission Tests + +test('EditServiceRequestTypeAssignments is gated with proper access control', function () { + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + actingAs($user) + ->get( + EditServiceRequestTypeAssignments::getUrl([ + 'record' => $serviceRequestType, + ]) + )->assertForbidden(); + + livewire(EditServiceRequestTypeAssignments::class, [ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ->assertForbidden(); + + $user->givePermissionTo('service_request_type.view-any'); + $user->givePermissionTo('service_request_type.*.update'); + + actingAs($user) + ->get( + EditServiceRequestTypeAssignments::getUrl([ + 'record' => $serviceRequestType, + ]) + )->assertSuccessful(); + + $request = collect(EditServiceRequestTypeAssignmentsRequestFactory::new()->withRandomTypeNotIncludingIndividual()->create()); + + livewire(EditServiceRequestTypeAssignments::class, [ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ->fillForm($request->toArray()) + ->call('save') + ->assertHasNoFormErrors(); + + assertEquals($request['assignment_type'], $serviceRequestType->fresh()->assignment_type->value); +}); + +test('EditServiceRequestTypeAssignments is gated with proper feature access control', function () { + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = false; + + $settings->save(); + + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $user->givePermissionTo('service_request_type.view-any'); + $user->givePermissionTo('service_request_type.*.update'); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + actingAs($user) + ->get( + EditServiceRequestTypeAssignments::getUrl([ + 'record' => $serviceRequestType, + ]) + )->assertForbidden(); + + livewire(EditServiceRequestTypeAssignments::class, [ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ->assertForbidden(); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + actingAs($user) + ->get( + EditServiceRequestTypeAssignments::getUrl([ + 'record' => $serviceRequestType, + ]) + )->assertSuccessful(); + + $request = collect(EditServiceRequestTypeAssignmentsRequestFactory::new()->withRandomTypeNotIncludingIndividual()->create()); + + livewire(EditServiceRequestTypeAssignments::class, [ + 'record' => $serviceRequestType->getRouteKey(), + ]) + ->fillForm($request->toArray()) + ->call('save') + ->assertHasNoFormErrors(); + + assertEquals($request['assignment_type'], $serviceRequestType->fresh()->assignment_type->value); +}); diff --git a/app/Features/ServiceRequestTypeAssignments.php b/app/Features/ServiceRequestTypeAssignments.php new file mode 100644 index 000000000..0ffb4eb02 --- /dev/null +++ b/app/Features/ServiceRequestTypeAssignments.php @@ -0,0 +1,47 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +namespace App\Features; + +use App\Support\AbstractFeatureFlag; + +class ServiceRequestTypeAssignments extends AbstractFeatureFlag +{ + public function resolve(mixed $scope): mixed + { + return false; + } +} diff --git a/app/Filament/Clusters/KnowledgeManagement.php b/app/Filament/Clusters/KnowledgeManagement.php index d362e48f3..df4b3467b 100644 --- a/app/Filament/Clusters/KnowledgeManagement.php +++ b/app/Filament/Clusters/KnowledgeManagement.php @@ -44,5 +44,9 @@ class KnowledgeManagement extends Cluster protected static ?string $navigationGroup = 'Product Administration'; + protected static ?string $navigationLabel = 'Knowledge Base'; + + protected static ?string $clusterBreadcrumb = 'Knowledge Base'; + protected static ?int $navigationSort = 7; } diff --git a/app/Filament/Forms/Components/Heading.php b/app/Filament/Forms/Components/Heading.php new file mode 100644 index 000000000..4db85d0c1 --- /dev/null +++ b/app/Filament/Forms/Components/Heading.php @@ -0,0 +1,102 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +namespace App\Filament\Forms\Components; + +use Closure; +use Filament\Forms\Components\Component; + +class Heading extends Component +{ + protected mixed $content = null; + + protected string | Closure | null $defaultView = 'filament.forms.components.heading-one'; + + protected function setUp(): void + { + parent::setUp(); + + $this->dehydrated(false); + + $this->columnSpanFull(); + } + + public static function make(): static + { + $static = app(static::class); + $static->configure(); + + return $static; + } + + public function one(): static + { + $this->view = 'filament.forms.components.heading-one'; + + return $this; + } + + public function two(): static + { + $this->view = 'filament.forms.components.heading-two'; + + return $this; + } + + public function three(): static + { + $this->view = 'filament.forms.components.heading-three'; + + return $this; + } + + public function content(mixed $content): static + { + $this->content = $content; + + return $this; + } + + public function getId(): string + { + return parent::getId() ?? $this->getStatePath(); + } + + public function getContent(): mixed + { + return $this->evaluate($this->content); + } +} diff --git a/app/Filament/Forms/Components/Paragraph.php b/app/Filament/Forms/Components/Paragraph.php new file mode 100644 index 000000000..015d112f6 --- /dev/null +++ b/app/Filament/Forms/Components/Paragraph.php @@ -0,0 +1,81 @@ + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +*/ + +namespace App\Filament\Forms\Components; + +use Closure; +use Filament\Forms\Components\Component; + +class Paragraph extends Component +{ + protected mixed $content = null; + + protected string | Closure | null $defaultView = 'filament.forms.components.paragraph'; + + protected function setUp(): void + { + parent::setUp(); + + $this->dehydrated(false); + + $this->columnSpanFull(); + } + + public static function make(): static + { + $static = app(static::class); + $static->configure(); + + return $static; + } + + public function content(mixed $content): static + { + $this->content = $content; + + return $this; + } + + public function getId(): string + { + return parent::getId() ?? $this->getStatePath(); + } + + public function getContent(): mixed + { + return $this->evaluate($this->content); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index ef927e5cc..5b9fc7fd0 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -73,6 +73,7 @@ use AidingApp\ServiceManagement\Models\ChangeRequestType; use Illuminate\Contracts\Translation\HasLocalePreference; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use AidingApp\ServiceManagement\Models\ServiceRequestType; use AidingApp\InAppCommunication\Models\TwilioConversation; use AidingApp\Engagement\Models\Concerns\HasManyEngagements; use AidingApp\Timeline\Models\Contracts\HasFilamentResource; @@ -306,6 +307,11 @@ public function teams(): BelongsToMany ->withTimestamps(); } + public function serviceRequestTypeIndividualAssignment(): HasMany + { + return $this->hasMany(ServiceRequestType::class, 'assignment_type_individual_id', 'id'); + } + public function assistantChatMessageLogs(): HasMany { return $this->hasMany(AssistantChatMessageLog::class); diff --git a/resources/views/filament/forms/components/heading-one.blade.php b/resources/views/filament/forms/components/heading-one.blade.php new file mode 100644 index 000000000..c594b4871 --- /dev/null +++ b/resources/views/filament/forms/components/heading-one.blade.php @@ -0,0 +1,42 @@ +{{-- + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +--}} + +

merge($getExtraAttributes(), escape: false)->class(['text-2xl font-bold tracking-tight text-gray-950 dark:text-white sm:text-3xl']) }}> + {{ $getContent() }} +

+
diff --git a/resources/views/filament/forms/components/heading-three.blade.php b/resources/views/filament/forms/components/heading-three.blade.php new file mode 100644 index 000000000..c9b213bdc --- /dev/null +++ b/resources/views/filament/forms/components/heading-three.blade.php @@ -0,0 +1,42 @@ +{{-- + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +--}} + +

merge($getExtraAttributes(), escape: false)->class(['text-sm font-semibold text-gray-950 dark:text-white sm:text-base']) }}> + {{ $getContent() }} +

+
diff --git a/resources/views/filament/forms/components/heading-two.blade.php b/resources/views/filament/forms/components/heading-two.blade.php new file mode 100644 index 000000000..03d43ec29 --- /dev/null +++ b/resources/views/filament/forms/components/heading-two.blade.php @@ -0,0 +1,42 @@ +{{-- + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +--}} + +

merge($getExtraAttributes(), escape: false)->class(['text-xl font-semibold text-gray-950 dark:text-white sm:text-2xl']) }}> + {{ $getContent() }} +

+
diff --git a/resources/views/filament/forms/components/paragraph.blade.php b/resources/views/filament/forms/components/paragraph.blade.php new file mode 100644 index 000000000..ca88cd2d6 --- /dev/null +++ b/resources/views/filament/forms/components/paragraph.blade.php @@ -0,0 +1,43 @@ +{{-- + + + Copyright © 2016-2024, Canyon GBS LLC. All rights reserved. + + Aiding App™ is licensed under the Elastic License 2.0. For more details, + see + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Aiding App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + or contact us via email at legal@canyongbs.com. + + +--}} + +

merge($getExtraAttributes(), escape: false)->class(['text-sm text-gray-950 dark:text-white']) }}> + {{ $getContent() }} +

+