'required|integer', 'item_type' => 'required|string', 'supplier_id' => 'nullable|integer', 'maintenance_type_id' => 'required|integer|exists:maintenance_types,id', 'name' => 'required|max:100', 'is_warranty' => 'boolean', 'start_date' => 'required|date', 'expected_completion_date' => 'date|nullable|after_or_equal:start_date', 'notes' => 'string|nullable', 'cost' => 'numeric|nullable|gte:0|max:99999999999999999.99', 'url' => 'nullable|url|max:255', 'responsible_party_id' => 'nullable|integer|exists:users,id', 'completed_by' => 'nullable|integer|exists:users,id', 'completed_at' => 'nullable|date|after_or_equal:start_date|before_or_equal:now', ]; protected $casts = [ 'start_date' => 'datetime', 'expected_completion_date' => 'datetime', 'completed_at' => 'datetime', ]; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', // Polymorphic item FK. Legacy asset_id is kept fillable so // Maintenance::create(['asset_id' => ...]) still works via the // setAssetIdAttribute mutator that forwards to item_id + // item_type=Asset. New callers should pass item_id + item_type // directly. 'item_id', 'item_type', 'asset_id', 'supplier_id', 'asset_maintenance_type', 'maintenance_type_id', 'is_warranty', 'start_date', 'expected_completion_date', // Legacy alias for expected_completion_date. Kept fillable so an // API v1 caller doing Maintenance::create(['completion_date' => // ...]) or ->update(...) still routes through the mutator that // forwards to the renamed column. 'completion_date', 'asset_maintenance_time', 'notes', 'cost', 'url', 'checked_out_to_id', 'checked_out_to_type', 'responsible_party_id', 'completed_at', 'completed_by', ]; use Searchable; /** * The attributes that should be included when searching the model. * * @var array */ protected $searchableAttributes = [ 'name', 'notes', 'cost', 'start_date', 'expected_completion_date', ]; /** * The relations and their attributes that should be included when searching the model. * * @var array */ protected $searchableRelations = [ 'asset' => ['name', 'asset_tag', 'serial'], 'asset.model' => ['name', 'model_number'], 'asset.supplier' => ['name'], 'asset.status' => ['name'], 'asset.company' => ['name'], 'asset.location' => ['name'], 'asset.defaultLoc' => ['name'], 'supplier' => ['name'], 'adminuser' => ['first_name', 'last_name', 'display_name'], 'maintenanceType' => ['name'], ]; public function getCompanyableParents() { return ['asset']; } /** * getImprovementOptions * * @return array * * @author Vincent Sposato * * @version v1.0 */ public static function getImprovementOptions() { return [ trans('admin/maintenances/general.maintenance') => trans('admin/maintenances/general.maintenance'), trans('admin/maintenances/general.repair') => trans('admin/maintenances/general.repair'), trans('admin/maintenances/general.upgrade') => trans('admin/maintenances/general.upgrade'), trans('admin/maintenances/general.pat_test') => trans('admin/maintenances/general.pat_test'), trans('admin/maintenances/general.calibration') => trans('admin/maintenances/general.calibration'), trans('admin/maintenances/general.software_support') => trans('admin/maintenances/general.software_support'), trans('admin/maintenances/general.hardware_support') => trans('admin/maintenances/general.hardware_support'), trans('admin/maintenances/general.configuration_change') => trans('admin/maintenances/general.configuration_change'), ]; } public function isDeletable() { return Gate::allows('delete', $this); } public function setIsWarrantyAttribute($value) { if ($value == '') { $value = 0; } $this->attributes['is_warranty'] = $value; } public function setCostAttribute($value) { $value = Helper::ParseCurrency($value); if ($value == 0) { $value = null; } $this->attributes['cost'] = $value; } public function setNotesAttribute($value) { if ($value == '') { $value = null; } $this->attributes['notes'] = $value; } public function setExpectedCompletionDateAttribute($value) { if ($value == '' || $value == '0000-00-00' || $value == '0000-00-00 00:00:00') { $value = null; } $this->attributes['expected_completion_date'] = $value; } /** * Legacy alias for the old `completion_date` field name (renamed to * `expected_completion_date` to match the "Expected Completion" UI * label). Kept so API v1 consumers that still write to the old key * — either as a mass-assigned Eloquent create/update or as raw HTTP * form field — keep working. The API transformer emits both keys * on read (see MaintenancesTransformer) for the same reason. */ public function setCompletionDateAttribute($value) { $this->setExpectedCompletionDateAttribute($value); } public function getCompletionDateAttribute() { return $this->expected_completion_date; } /** * asset * Get asset for this improvement * * @return mixed * * @author Vincent Sposato * * @version v1.0 */ /** * Polymorphic parent - an Asset (historically), and potentially * an Accessory or other checkoutable model going forward. Every * new caller should use ->item; ->asset() below stays as a * backward-compat alias that returns the parent only when it IS * an Asset (returns null for other item_types). */ public function item() { return $this->morphTo()->withTrashed(); } /** * Legacy alias for ->item kept so existing blades, transformers, * and controllers using $maintenance->asset or ->with('asset') * keep working during the transition to polymorphic items. * No item_type WHERE guard here because it would attach to the * assets-table subquery during eager loading (Eloquent scopes the * belongsTo child query at the parent side) and reference a * non-existent maintenances.item_type column from the assets * table context. Every existing maintenance has item_type = Asset * per the rename migration's backfill, so the plain belongsTo * still returns the right rows today. Once accessories actually * carry maintenances, callers should switch to ->item. */ public function asset() { return $this->belongsTo(Asset::class, 'item_id') ->withTrashed(); } /** * Legacy accessor for the old asset_id column. Returns item_id * only when the polymorphic parent is an Asset so it doesn't * misrepresent a maintenance attached to an accessory as an * asset-scoped one. */ public function getAssetIdAttribute() { return $this->item_type === Asset::class ? $this->attributes['item_id'] ?? null : null; } /** * Legacy mutator - Maintenance::create(['asset_id' => X]) still * works. Sets item_id + pins item_type to Asset since callers * using asset_id are, by definition, targeting an Asset. */ public function setAssetIdAttribute($value): void { $this->attributes['item_id'] = $value; $this->attributes['item_type'] = Asset::class; } /** * Get the maintenance logs * * @author [A. Gianotto] [] * * @since [v8.2.2] * * @return Relation */ public function assetlog() { return $this->hasMany(Actionlog::class, 'item_id') ->where('item_type', '=', self::class) ->orderBy('created_at', 'desc') ->withTrashed(); } public function supplier() { return $this->belongsTo(Supplier::class, 'supplier_id') ->withTrashed(); } public function maintenanceType() { return $this->belongsTo(MaintenanceType::class, 'maintenance_type_id'); } public function responsibleParty() { return $this->belongsTo(User::class, 'responsible_party_id') ->withTrashed(); } public function completedByUser() { return $this->belongsTo(User::class, 'completed_by') ->withTrashed(); } public function checkedOutTo() { return $this->morphTo('checked_out_to'); } /** * Fields published to the calendar_events index table via the * HasCalendarEvents trait. A single range event per maintenance * anchored on start_date -> expected_completion_date. The * completed_at column is intentionally NOT its own event today - * "completed" is a state, not a scheduled item, and the read * path can flip a completed marker via the source model. Add * more entries here if a maintenance ever needs multiple event * markers on the calendar. */ public function calendarEventDefinitions(): array { return [ [ 'field' => 'start_date', 'event_type' => 'maintenance.start', 'end_field' => 'expected_completion_date', 'all_day' => true, ], ]; } public function journal() { return $this->assetlog()->where('action_type', '=', 'note added'); } public function getDisplayNameAttribute() { return $this->name; } public function newEloquentBuilder($query): MaintenanceQueryBuilder { return new MaintenanceQueryBuilder($query); } }