Karbantartás menedzsment rendszer - 9. rész: Hibák bejelentése munkalapokkal

Attila | 2023. 11. 03. 18:38 | Olvasási idő: 7 perc

Címkék: #Digitalizáció (Digitalization) #Eloquent #Engedélyeztetés (Authorization) #Érvényesítés (Validation) #Felhasználók (Users) #Filament #Ipar 4.0 (Industry 4.0) #Jogosultság (Permission) #Laravel #Laravel 10 #Szerepkörök (Roles) #Telepítés (Installation) #Többnyelvűsítés (Localization) #Űrlap (Form)

A hibákat leggyakrabban az operátor személyek, azok a dolgozók veszik észre, akik ténylegesen napi szinten kapcsolatban vannak a gépekkel, berendezésekkel, így nekik is biztosítani kell a lehetőséget, hogy be tudják jelenteni az észlelt problémákat. Ezeket úgynevezett munkalapokon keresztül tudják bejelenteni.
worksheets-online

Bevezetés

Az egyik célunkat szeretnénk elérni a munkalapok feldolgozása során. Az volt az elvárás, hogy a gépkezelők tudjanak bejelenteni hibákat a berendezésekkel kapcsolatban és ezeket strukturáltan tudja eltárolni a Karbantartás Menedzsment Rendszer. Ezt még azzal is kiegészítjük, hogy a karbantartó szerepkörű felhasználok meg tudják határozni, hogy az a hiba mennyire okoz problémát, illetve ebből adódóan mennyire sürgős a javítása. Nyilván az a legsürgősebb javítási feladat, ami miatt esetleg megállhatna a termelés. A karbantartók tehát sürgősségi szintet, határidőt is tudnak definiálni majd a munkalapokon. Ezen felül a munkalapok segítségével több mindent is nyomon lehet majd követni a rendszer segítségével:

  • Mi volt a konkrét hiba a berendezésen?
  • Lehet digitális fényképet feltölteni a hibákról.
  • A hibabejelentő személyeket eltároljuk, de rajtuk kívül a hiba javítását elvállaló karbantartó személyét is.
  • Láthatjuk majd, hogy a hiba bejelentésétől kezdve mennyi időre volt szükség az egyes hiba típusonként a javításra.
  • Tudni fogjuk, hogy melyik berendezéssel mennyi és milyen típusú (sürgősségű) hibák adódtak, ez a későbbiekben adatelemzésnél nagyon hasznos lehet a számunkra.
  • De a felsoroltakon kívül, számos lehetőségünk lesz majd még az adatelemzésre a rendszerben.

Tervezés szempontjából a munkalapokat tároló adattábla (bal felül: worksheets) így illeszkedhet be az adatbázis szerkezetébe: az adatbázis diagram most releváns részei:

Megjegyzés: a Spatie Laravel Permission csomagnak éppen a munkám során jött ki a legfrissebb fő verziója (6-os), így azt érdemes frissíteni úgy, hogy a composer.json fájlban a "require" szekcióba beírjuk ezt a megfelelő helyre: "spatie/laravel-permission": "^6.0" majd után composer update paranccsal érvényre is juttatjuk a legfrissebb csomagverziók használatát.


Munkalap - Worksheet

Migrációs fájl

Hozzuk létre a Model-t és a migrációs fájlt (controller-re és factory-ra nincs most még szükségünk):

php artisan make:model Worksheet -m

Építsük fel az adattábla szerkezetét a migrációs fájlban:

public function up(): void
{
  Schema::create('worksheets', function (Blueprint $table) {
    $table->id();
    $table->enum('priority', ['Normál', 'Sürgős', 'Leálláskor'])->default('Normál');

    $table->text('description');
    $table->date('due_date')->nullable();
    $table->date('finish_date')->nullable();

    $table->foreignId('device_id')->constrained()->onUpdate('no action')->onDelete('no action');

    $table->unsignedBigInteger('creator_id');
    $table->foreign('creator_id')->references('id')->on('users')->onUpdate('no action')->onDelete('no action');

    $table->unsignedBigInteger('repairer_id')->nullable();
    $table->foreign('repairer_id')->references('id')->on('users')->onUpdate('no action')->onDelete('no action');

    $table->text('attachments');

    $table->text('comment')->nullable();
    $table->timestamps();
  });
}

Magyarázatok:

  • A sürgősségi szinteket (priority) enum, vagyis felsorolás oszloptípussal definiáljuk (ami akkor hasznos, ha egy aránylag szűk értékkészletből adódnak az adott mező lehetséges értékei, mint például, ha a napok vagy a hónapok, esetleg a szerepkörök nevét szeretnénk így tárolni), ennek a mezőnek a kitöltése mindig kötelező, de alapértelmezetten Normál szintet határozunk meg a hibáknak.
  • A hibáról teszünk be egy szöveges leírást (description), ami kötelező, de az oszlopszerkezet végén egy nem kötelező megjegyzést (comment) is lehet hozzáfűzni majd a munkalaphoz.
  • Amikor a gépkezelő létrehozza a munkalapot (hiba észrevétele után), akkor ő még nem tudja, vagyis nem határozhatja meg, hogy milyen határidővel (due_date) történjen meg a hiba javítása, illetve a bejelentéskor még azt sem tudja jelezni, hogy mikor is fog megtörténni a tényleges kijavítás (finish_date).
  • Idegen kulcs mezők:
    • Minden munkalap egy berendezéshez fog tartozni, ennek megfelelően egy adott berendezéshez kötjük a munkalapot az adatbázisban. Berendezés törlésénél és frissítésénél a munkalapoknál ne történjen változás. Ezen a ponton érdemes elgondolkodni azon, hogy a berendezés típusoknál, berendezéseknél, dokumentumoknál is "átálljunk" a soft delete funkció alkalmazására, vagyis arra, hogy ne törlődjenek véglegesen az elemeink az adattáblákból, hanem csak "törlésre" jelölhessük ki őket, amit aztán az adminisztrátorok esetleg vissza is tudjanak majd állítani, ha mégsem szeretnénk törölni őket. Egyelőre viszont itt azt a logikát kövessük, hogy ne történjen semmi (no action), ha a külső kulcsos hivatkozott elemmel valami történne (frissítésre vagy törlésre kerülne).
    • A felhasználók táblához két "irányból" is kapcsolódjunk: az egyik a munkalapot létrehozó felhasználó legyen, a másik pedig a karbantartást elvégző személy legyen.
  • A hibáról mellékelt képet, képeket (akár többet!) az attachments mezőben fogjuk eltárolni.

Futtathatjuk a migrálást:

php artisan migrate

Model osztály és a kiegészítő felsorolás (enum) osztály

A migrációs fájl alapján gyűjtsük össze, hogy milyen mezőket kell engednünk kitölteni! A $fillable mező tömbjét bővítsük ki az alábbiak szerint:

protected $fillable = [
  'priority',
  'description',
  'due_date',
  'finish_date',
  'device_id',
  'creator_id',
  'repairer_id',
  'attachments',
  'comment',
];

Két mező "kasztolását" (adatbázis mezőjének a típusát másképp használjuk a PHP programozás során, mindezt automatikusan) végezzük el a Model osztályban:

  1. priority: adattáblában enum típus, de mi egy PHP, Laravel és Filament specifikus osztállyal definiáljuk ezt a programkódunkban (további információ erről itt: https://laravel.com/docs/10.x/eloquent-mutators#enum-casting).
  2. attachments: adattáblában szöveges érték, de mi kezeljük tömbként.
protected $casts = [
  'priority' => WorksheetPriority::class,
  'attachments' => 'array',
];

A WorksheetPriority-t az app / Enums új könyvtárban kell majd létrehozni WorksheetPriority.php fájlnévvel, amit itt a Worksheet Model osztályban importálni is kell. A WorksheetPriority.php osztály tartalmát úgy kell összeállítani, hogy előtte a Filament specifikus dolgokat áttekintjük a dokumentációban: https://filamentphp.com/docs/3.x/support/enums Az új WorksheetPriority.php fájl tartalmaz ezekután:

<?php

namespace App\Enums;

use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Contracts\HasColor;

enum WorksheetPriority: string implements HasLabel, HasColor, HasIcon
{
  case NORMAL = 'Normál';
  case SURGOS = 'Sürgős';
  case LEALLASKOR = 'Leálláskor';

  public function getLabel(): ?string
  {
    return match ($this) {
      self::NORMAL => 'Normál',
      self::SURGOS => 'Sürgős',
      self::LEALLASKOR => 'Leálláskor',
    };
  }

  public function getColor(): string|array|null
  {
    return match ($this) {
      self::NORMAL => 'warning',
      self::SURGOS => 'danger',
      self::LEALLASKOR => 'gray',
    };
  }

  public function getIcon(): ?string
  {
    return match ($this) {
      self::NORMAL => 'heroicon-m-exclamation-circle',
      self::SURGOS => 'heroicon-m-exclamation-triangle',
      self::LEALLASKOR => 'heroicon-m-sun',
    };
  }
}

Az osztályon belül definiáljuk a három sürgősségi szintet, mint prioritási lehetőséget. A metódusokkal pedig definiáljuk a címke nevét (getLabel()), a színét (getColor()) és végül az ikonját (getIcon()), amik majd látszódni fognak a Filament erőforrás táblázatában.

Eloquent kapcsolatok mindkét oldalról (Worksheet és Device / User)

Kössük hozzá először a Worksheet Model osztályt a "társaihoz"! Mindegyik kapcsolat 1-n típusú.

public function device(): BelongsTo
{
  return $this->belongsTo(Device::class);
}

public function creator(): BelongsTo
{
  return $this->belongsTo(User::class, 'creator_id'); // aki létrehozta a munkalapot
}

public function repairer(): BelongsTo
{
  return $this->belongsTo(User::class, 'repairer_id'); // aki karbantartó!
}

A BelongsTo osztályt importálni kell a fájl tetején, az odaírt kommentek pedig beszédesek, hogy melyik-melyik kapcsolatért felelős.

Másik oldalról, mindezt a Device osztályban:

public function worksheets(): HasMany
{
  return $this->hasMany(Worksheet::class);
}

Majd a User osztályban:

public function creators_worksheets(): HasMany
{
  return $this->hasMany(Worksheet::class, 'creator_id');
}

public function repairers_worksheets(): HasMany
{
  return $this->hasMany(Worksheet::class, 'repairer_id');
}

A HasMany osztályt importálni kell a fájl tetején.


Munkalap (Worksheet) erőforrás

Hozzuk létre a munkalap erőforrást úgy, hogy generáljuk a létrehozott adattáblájanak szerkezete alapján. Azonban, ha megpróbálnánk ezt így létrehozni, akkor figyelmeztetést kapnánk, mivel alapértelmezetten a rendszer nem ismeri az enum adattábla típust.

Maga az erőforrás létrejönne, viszont nem töltené fel tartalommal az űrlap és táblázat megjelenítő és beállító metódusokat. Emiatt néhány módosítást végre kell hajtanunk a megfelelő működéshez. Nyissuk meg az app / Providers / AppServiceProvider.php fájlt és annak a boot() metódusát bővítsük:

DB::connection()
  ->getDoctrineSchemaManager()
  ->getDatabasePlatform()
  ->registerDoctrineTypeMapping('enum', 'string');

Hozzá pedig importáljuk be a DB facade-ot a fájl elején: use Illuminate\Support\Facades\DB;

Utána telepítsük a doctrine/dbal csomagot a generálás zökkenőmentessé tétele miatt:

composer require doctrine/dbal --dev

Majd következhet az erőforrás létrehozása generálással (adattábla alapján):

php artisan make:filament-resource Worksheet --generate

Így a form() és table() metódusok és lényegi tartalommal töltődtek fel az újonnan létrejövő WorksheetResource.php fájlban.

Alapbeállítások

Bővítsük ki a WorksheetResource osztályt az alábbi alapbeállításokkal:

protected static ?string $navigationIcon = 'heroicon-o-document-plus';

public static function getNavigationGroup(): string
{
  return __('module_names.navigation_groups.failure_report');
}

protected static ?int $navigationSort = 7;

public static function getModelLabel(): string
{
  return __('module_names.worksheets.label');
}

public static function getPluralModelLabel(): string
{
  return __('module_names.worksheets.plural_label');
}

A hiányzó fordítás részeket a module names szótárban bővítsük ki: a 'navigation_groups'-hoz adjuk hozzá a 'failure_report'-ot 'Failure report' névvel angolul és 'Hibabejelentés' névvel magyarul. Továbbá egy 'worksheets' szekciót a 'documents'-hez hasonlóan.

A megszokott módon pedig a CreateWorksheet és EditWorksheet osztályokhoz adjuk hozzá a getRedirectUrl() metódust, amellyel a mentés után elirányítjuk a felhasználót a munkalapok index oldalára.

Űrlap

Az űrlap lesz a feladatban a legbonyolultabb: azért is ez, mivel különböző fajta ellenőrzéseket, érvényesítéseket kell a bemeneti elemekhez illeszteni, illetve a bemutató további részében jogosultságokhoz is kötjük majd az egyes elemek kitöltésének / szerkesztésének engedélyezését, így ami ebben az alfejezetben elkészül űrlap, az még nem a végleges megoldása lesz ennek. Itt van az űrlap első változata, alatta pedig az egyes mezők magyarázatai következnek:

public static function form(Form $form): Form
{
  return $form
    ->schema([
      Forms\Components\Section::make()->schema([
        Forms\Components\Select::make('device_id')->label(__('module_names.devices.label'))
          ->relationship('device', 'name')
          ->required(),
        Forms\Components\Select::make('creator_id')->label(__('fields.creator'))
          ->relationship('creator', 'name')
          ->required(),
        Forms\Components\Select::make('repairer_id')->label(__('fields.repairer'))
          ->options(User::role('repairer')->get()->pluck('name', 'id')),
        Forms\Components\Select::make('priority')->label(__('fields.priority'))
          ->options(WorksheetPriority::class)
          ->default('Normal')
          ->required(),
        Forms\Components\Textarea::make('description')->label(__('fields.description'))
          ->required()
          ->maxLength(65535)
          ->columnSpanFull(),
        Forms\Components\DatePicker::make('due_date')->label(__('fields.due_date'))
          ->minDate(now()),
        Forms\Components\DatePicker::make('finish_date')->label(__('fields.finish_date'))
          ->minDate(now())
          ->default(now()),
        Forms\Components\FileUpload::make('attachments')->label(__('fields.attachments'))
          ->required()
          ->multiple()
          ->preserveFilenames()
          ->openable()
          ->downloadable()
          ->columnSpanFull(),
        Forms\Components\Textarea::make('comment')->label(__('fields.note'))
          ->maxLength(65535)
          ->columnSpanFull(),
      ])
    ]);
}

Részletezések:

  • Az egész űrlapot, ahogy korábban is tettük, egy szekcióba foglaljuk.
  • Minden munkalapot egy berendezéshez kapcsolunk, ez kötelezően kitöltendő mező.
  • A karbantartói lenyíló listában csak azokat a felhasználókat jelenítjük meg, akik karbantartói szerepkörrel rendelkeznek.
  • A prioritások a WorksheetPriority Enum osztályból érkeznek, alapból Normál priorítást állítunk be.
  • A hiba leírása kötelező mező.
  • Határidő és befejezés dátuma mezőknél minimum az aktuális napi dátumot állítjuk be az ellenőrzésben.
  • A fájl csatolása itt alapvetően a képeket jelenti, amit az operátor (vagy más gépkezelő személy) a hibáról készíthet és feltölthet. Több képet is mellékelhet itt, amelyek utána megnyithatóak és letölthetőek lesznek.
  • Az űrlap végén lévő egyéni megjegyzés nem kötelező, de a hiba bejelentőjének itt adunk lehetőséget arra, hogy leírhassa a saját véleményét a hibáról, vagy karbantartási javaslatot írjon a címzettnek.

Tipp: ha úgy szeretnénk beállítani, hogy lehessen késés is a karbantartásnál, vagyis a befejezés dátuma későbbi legyen, mint a megadott határidő, akkor ezt is érdemes lekezelni valahogy. Például ha későbbi a befejezés dátuma, mint a határidő dátuma, akkor automatikusan kerüljön majd kiszűrésre, hogy ez egy késedelmes feladat; esetleg legyen egy külön opció (bool) eltárolva az adattáblában, hogy késős-e a feladat, mert akkor be tudunk állítani egy maximális dátumot a befejezés dátumára, ami a határidő lesz és csak akkor engedünk ennél későbbi dátumot beállítani neki, ha átváltja a kapcsolót a karbantartó, hogy ez már egy késedelmes javítás dokumentálása.

Táblázat

A táblázatban a hibás berendezés nevét, a hiba rövid leírását, a karbantartás prioritását és a létrehozás dátumát (idejét) helyezzük el láthatóan, a többi mező is maradhat, viszont alapértelmezetten rejtsük el őket. Az erőforrás automatikus generálása már jó néhányat mezőt elhelyezett a table() columns()-t érintő metódus visszatérési tömbjében ezek közül, úgyhogy nagyjából csak válogatásra és esetleg pontosításra van szükségünk.

Tables\Columns\TextColumn::make('id')
  ->searchable()
  ->sortable(),
Tables\Columns\TextColumn::make('created_at')->label(__('fields.created_at'))
  ->dateTime('Y-m-d H:i')
  ->searchable()
  ->sortable(),
Tables\Columns\TextColumn::make('device.name')->label(__('module_names.devices.label'))
  ->numeric()
  ->searchable()
  ->sortable(),
Tables\Columns\TextColumn::make('description')->label(__('fields.description'))
  ->limit(30)
  ->searchable()
  ->sortable(),
Tables\Columns\TextColumn::make('priority')->label(__('fields.priority'))
  ->searchable()
  ->sortable(),
Tables\Columns\TextColumn::make('creator.name')->label(__('fields.creator'))
  ->numeric()
  ->searchable()
  ->sortable()
  ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('repairer.name')->label(__('fields.repairer'))
  ->numeric()
  ->searchable()
  ->sortable()
  ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('due_date')->label(__('fields.due_date'))
  ->date('Y-m-d')
  ->searchable()
  ->sortable()
  ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('finish_date')->label(__('fields.finish_date'))
  ->date('Y-m-d')
  ->searchable()
  ->sortable()
  ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('updated_at')->label(__('fields.updated_at'))
  ->dateTime('Y-m-d H:i')
  ->searchable()
  ->sortable()
  ->toggleable(isToggledHiddenByDefault: true),

Az itt látható oszlop tulajdonságokat már mind ismerjük, kivétel talán a description (leírás) oszlopnál látható limit() segédmetódust, amelyben 30 karakterben maximalizáltuk azt a hosszt, amelyet a táblázat megmutat nekünk a hiba leírásáról. Ha ennél többet szeretnénk megmutatni, akkor át kell állítani a paraméter értékét, de ha valaki az adatsor értékeinek megtekintésére kattint, akkor úgyis részletesen láthatja a mezők értékeit. Ehhez azonban hozzuk létre a munkalapok megtekintés nézetét:

php artisan make:filament-page ViewWorksheet --resource=WorksheetResource --type=ViewRecord

Így létrejön a WorksheetResource / Pages mappában a ViewWorksheet.php fájl.

Ha pedig a WorksheetResource.php getPages() metódusának visszatérési tömbjéhez hozzáadjuk az alábbi kiemelt sort:

return [
  'index' => Pages\ListWorksheets::route('/'),
  'create' => Pages\CreateWorksheet::route('/create'),
  'view' => Pages\ViewWorksheet::route('/{record}'),
  'edit' => Pages\EditWorksheet::route('/{record}/edit'),
];

Így most már a table() metódusban a visszatérési $table objektum actions() metódusához hozzáadhatjuk (elsőként) a ViewAction-t és akkor a táblázatos nézetben egyből fel fog tűnni a Megtekintés link a sorokban.

->actions([
  Tables\Actions\ViewAction::make(),
  Tables\Actions\EditAction::make(),
])

Szeretnénk viszont megfordítani a sorrendet, így adjuk hozzá a table() metódus visszatérési $table tömbjéhez ezt a metódushívást (első paraméter az, hogy melyik oszlop szerint rendezzünk, alapértelmezetten növekvőben, hacsak nem adjuk meg a második paraméterben, hogy visszafelé rendezzük):

->defaultSort('created_at', 'desc')

Így a legfrissebb munkalapok fognak legelőször látszódni a táblázatban. Az eredmény itt látható mintaadatok felvitele után:

A prioritás szerinti szűrőket (tab-okat) helyezzük el még a táblázat felett. A prioritás értékei a WorksheetPriority enum osztály alapján kerülnek meghatározásra. A ListWorksheets.php -hoz adjuk hozzá még a tab-os szűrőt és az alapértelmezetten aktív tab-ot.

public function getTabs(): array
{
  $tabs = [
    'all' => Tab::make()->label(__('fields.all'))
      ->icon('heroicon-o-list-bullet')
      ->badge(Worksheet::query()->count()),
  ];

  $priorities = array_column(WorksheetPriority::cases(), 'value');

  foreach ($priorities as $priority) {
    $tabs[$priority] = Tab::make()->label($priority)
      ->modifyQueryUsing(
        fn (Builder $query) => $query
          ->where('priority', $priority)
      )
      ->badge(
        Worksheet::query()
          ->where('priority', $priority)
          ->count()
      )
      ->icon(WorksheetPriority::tryFrom($priority)?->getIcon());
  }
  return $tabs;
}

public ?string $activeTab = 'all';

A WorksheetPriority Enum osztályt használtuk ehhez, így nyertük ki a prioritások értékeit, aztán pedig az értékeknek megfelelő címkéket és ikonokat. Így most már a prioritás szerinti szűrők is működnek:


Engedélyeztetés

A munkalapok menedzselése során különösen fontos, hogy pontosan átgondoljuk, melyik mezőhöz ki férhet hozzá, ki láthatja, illetve ki szerkesztheti és mikor.

WorksheetPolicy

Hozzuk létre, ahogy már korábban is tettük:

php artisan make:policy WorksheetPolicy --model=Worksheet

Az osztály lényegi tartalma pedig:

/**
 * Determine whether the user can view any models.
 */
public function viewAny(User $user): bool
{
  return $user->can('read worksheets');
}

/**
 * Determine whether the user can view the model.
 */
public function view(User $user, Worksheet $worksheet): bool
{
  return $user->can('read worksheets');
}

/**
 * Determine whether the user can create models.
 */
public function create(User $user): bool
{
  return $user->can('create worksheets');
}

/**
 * Determine whether the user can update the model.
 */
public function update(User $user, Worksheet $worksheet): bool
{
  return $user->can('update worksheets');
}

/**
 * Determine whether the user can delete the model.
 */
public function delete(User $user, Worksheet $worksheet): bool
{
  return $user->can('delete worksheets');
}

Az újonnan létrehozott WorksheetPolicy osztályt érdemes már most bekötni a rendszerbe az AuthServiceProvider osztály $policies tömbjének bővítésével:

Worksheet::class => WorksheetPolicy::class,

Az importálásokról sem feledkezzünk meg az osztály előtt (Worksheet, WorksheetPolicy).

Ahogy említettem, érdemes átgondolni, hogy kinek mit engedélyezünk. A törlést leginkább csak az adminisztrátor felhasználóknak. A frissítést a karbantartóknak és az adminisztrátoroknak. A létrehozásnál már szóba jönnek az operátorok is, akiknek lehetőséget kell erre adni. Mindezeket a beállításokat az adminisztrációs felületen az adminisztrátor felhasználó végre tudja hajtani:

  1. Hozzá kell adni ezeket az új jogosultságokat a rendszerhez, majd ...
  2. a szerepkörök szerkesztésénél, új jogosultságokkal kell ellátni a szerepköröket az itt leírtak szerint: admin: mind a négy új jogosultság (create, read, update, delete worksheets), karbantartónál három új jogosultság (create, read, update worksheets), operátornál két új jogosultság (create, read worksheets).

Táblázat v2 !?

Inkább ne... Felmerülhet az az "issue", hogy ha az adott felhasználó, aki listázza a munkalapokat, nem rendelkezik "update worksheets" jogosultsággal, tehát nem karbantartó és nem adminisztrátor, csak operátor, akkor neki csak azt engedjük meg, hogy a saját maga által létrehozott munkalapokat láthassa. Ez azonban annyiból nem lenne jó, hogy akár különböző operátorok, ugyanahhoz a berendezéshez, ugyanazt a problémát többször, egymástól függetlenül lejelentik a karbantartóknak. Ezt természetesen nem szeretnénk.

Ha valaki mégis megtenné ezt, vagy valamilyen hasonló szűrést alkalmazna, akkor azt a WorksheetResource osztályban kellene definiálnia:

public static function getEloquentQuery(): Builder
{
  if (!auth()->user()->can('update worksheets')) {
    return parent::getEloquentQuery()->where('creator_id', auth()->user()->id);
  }
  return parent::getEloquentQuery();
}

Űrlap v2

Az űrlapot viszont módosítanunk kell több bemenetnél is:

  • device_id: berendezést engedjünk kiválasztani a bejelentő operátornak is, módosítani úgysem tudja létrehozás után csak a karbantartó és az adminisztrátor.
  • creator_id: a létrehozó személy azonosítója. Alapértelmezetten állítsuk be úgy, hogy ha operátor a létrehozó, akkor ő maga legyen ide elhelyezve és ne lehessen ezt módosítani, ha karbantartó vagy admin, akkor pedig ne legyen alapértelmezett értéke és módosítható legyen a számukra az értéke.
  • repairer_id: a karbantartó személyének kiválasztását tiltsuk is le az operátoroknak. Ezt a mezőt csak a karbantartó és az admin tudja módosítani, beállítani.
  • description: leírást meg kell adnia létrehozáskor az operátornak, utána szerkeszteni már nem fogja tudni.
  • priority: prioritást engedjünk kiválasztani a bejelentő operátornak is, módosítani úgysem tudja létrehozás után csak a karbantartó és az adminisztrátor.
  • due_date: javítási határidőt az operátor ne szabhasson meg a karbantartóknak, úgyhogy ennek a mezőnek a hozzáférését limitálni kell az operátor számára, neki ne is jelenítsük meg!
  • finish_date: az operátorok elől ezt már ne rejtsük el (láthassák, hogy mikor történt meg a javítás), viszont ne tudják szerkeszteni ezt a mezőt.
  • attachments: csak képet lehessen feltölteni és egy újdonságot is kipróbálhatunk! Ha a feltöltött kép fölé visszük az egeret, akkor alul megjelenik egy ceruza menüpont, amelyet kiválaszthatunk és szerkeszthetővé válik az a kép, amit esetleg a hibáról lefotóztunk.
  • note: a megjegyzés mezőnél nincs szükség további megszorításokra.

Mindezeket a beállításokat így végezhetjük el a form() metódusban:

public static function form(Form $form): Form
{
  return $form
    ->schema([
      Forms\Components\Section::make()->schema([
        Forms\Components\Select::make('device_id')->label(__('module_names.devices.label'))
          ->relationship('device', 'name')
          ->required(),
        Forms\Components\Select::make('creator_id')->label(__('fields.creator'))
          ->relationship('creator', 'name')
          ->default(!auth()->user()->can('update worksheets') ? auth()->user()->id : null)
          ->disabled(!auth()->user()->can('update worksheets') ? true : false)
          ->required(),
        Forms\Components\Select::make('repairer_id')->label(__('fields.repairer'))
          ->options(User::role('repairer')->get()->pluck('name', 'id'))
          ->disabled(!auth()->user()->can('update worksheets')),
        Forms\Components\Select::make('priority')->label(__('fields.priority'))
          ->options(WorksheetPriority::class)
          ->default('Normal')
          ->required(),
        Forms\Components\Textarea::make('description')->label(__('fields.description'))
          ->required()
          ->maxLength(65535)
          ->columnSpanFull(),
        Forms\Components\DatePicker::make('due_date')->label(__('fields.due_date'))
          ->hidden( ! auth()->user()->can('update worksheets'))
          ->minDate(now()),
        Forms\Components\DatePicker::make('finish_date')->label(__('fields.finish_date'))
          ->disabled(!auth()->user()->can('update worksheets'))
          ->minDate(now())
          ->default(now()),
        Forms\Components\FileUpload::make('attachments')->label(__('fields.attachments'))
          ->required()
          ->image()
          ->imageEditor()
          ->imageEditorAspectRatios([
            null,
            '16:9',
            '4:3',
            '1:1',
          ])
          ->imageEditorEmptyFillColor('#000000')
          ->imageEditorViewportWidth('1920')
          ->imageEditorViewportHeight('1080')
          ->multiple()
          ->preserveFilenames()
          ->openable()
          ->downloadable()
          ->columnSpanFull(),
        Forms\Components\Textarea::make('comment')->label(__('fields.note'))
          ->maxLength(65535)
          ->columnSpanFull(),
      ])
    ]);
}

A kép szerkesztési modal ablak így néz ki:

Az utóbbi módosításokkal több mezőt (creator_id, repairer_id, finish_date) is letiltottunk az operátor számára, és valamit el is rejtettünk előle, miközben valamelyik mezőnek a kitöltése kötelező a worksheets adattábla kényszerei miatt. Ezeket a kiegészítéseket a CreateWorksheet és EditWorksheet osztályokban tudjuk megtenni ahhoz, hogy ne okozhasson problémát az adattábla feltöltésekor vagy módosításakor. Kényszerek szempontjából csak a creator_id-vel kell foglalkoznunk, a többi mezző nem problémás. CreateWorksheet osztály új metódusa:

protected function mutateFormDataBeforeCreate(array $data): array
{
  if (!isset($data['creator_id']))
    $data['creator_id'] = auth()->user()->id;

  return $data;
}

Ha nincs beállítva a creator_id mező, mert az operátoroknak le van tiltva ennek módosítása már a létrehozáskor, akkor beállítjuk ennek az értéket az adattábla feltöltése előtt.

Ugyanezt kellene megtennünk szerkesztéskor is, viszont csak karbantartók és adminisztrátorok tudják szerkeszteni a munkalapot, akiknél ez a mező nincs letiltva az űrlapon, emiatt nem kell módosítanunk az EditWorksheet osztályt.


Összegzés, továbblépés

Ebben a részben a munkalapokkal foglalkoztam. Elég részletes űrlapot definiáltam nekik, amelyet aztán számos új technikával vérteztem fel validálási és engedélyeztetési oldalról. A fejezetben található programkód módosításokat ebben a GitHub commit-ben lehet megtalálni. (Megjegyzés: a Filament-et is érdemes felfrissíteni, ha már régebben használtuk, úgyhogy ezeket a módosításokat - csak CSS és JS fájlokat - is tartalmazza a GitHub commit.)

A munkalapokkal kapcsolatos rész nem ért itt még véget, folytatom még a finomhangolást, illetve rátérek a munkalapok kapcsolataira. Ezáltal látható lesz a berendezések "kórtörténete" (hiba- és javítási listája), a felhasználók látni fogják a saját munkalapjaikat, hogy hogyan alakult az általuk jelentett hiba javítása, kezelése. Majd rátérek még a widget-ekre is, hiszen a munkalapok sok értékes információt tartalmazhatnak a karbantartási részleg számára és a döntéshozóknak is.


Dolgozzunk együtt: egyéni oktatás, mentorálás, fejlesztés, tanácsadás

Ha egyéni oktatás, mentorálás, vagy fejlesztési projekt kapcsán szeretnél segítséget kérni tőlem, esetleg együttműködni velem, akkor keress meg a weboldalamon található elérhetőségeken keresztül!