*/ protected array $requiredTables = [ 'maintenance_equipment', 'maintenance_equipment_categories', 'maintenance_equipment_parts', 'maintenance_work_orders', 'maintenance_preventive_schedules', 'maintenance_spare_parts', 'maintenance_work_order_parts', 'maintenance_spare_part_consumptions', 'maintenance_failure_reports', 'maintenance_activity_logs', 'maintenance_notifications', 'maintenance_inspection_visits', 'maintenance_service_records', 'maintenance_part_replacements', 'maintenance_history_entries', 'maintenance_asset_documents', 'maintenance_checklist_templates', 'maintenance_production_lines', 'maintenance_part_catalogs', 'maintenance_service_providers', ]; /** @var array */ protected array $ensureMigrations = [ '2026_07_07_170000_ensure_missing_cmms_tables', '2026_07_07_160000_ensure_maintenance_lifecycle_tables', '2026_07_07_150000_ensure_maintenance_asset_documents_table', '2026_07_07_180000_fix_spare_parts_and_service_contracts', '2026_06_30_200005_fix_maintenance_morph_types', '2026_06_30_200006_inspection_visit_project_scope', '2026_06_30_200007_maintenance_client_and_scope', '2026_07_07_190000_fix_equipment_scope_columns', ]; public function assess(): array { $missing = []; $present = []; foreach ($this->requiredTables as $table) { if (Schema::hasTable($table)) { $present[] = $table; } else { $missing[] = $table; } } $score = count($this->requiredTables) > 0 ? (int) round((count($present) / count($this->requiredTables)) * 100) : 100; return [ 'healthy' => empty($missing), 'score' => $score, 'present_count' => count($present), 'required_count' => count($this->requiredTables), 'missing_tables' => $missing, 'present_tables' => $present, 'integrations' => $this->assessIntegrations(), ]; } public function repair(): array { $before = $this->assess(); $ran = []; $errors = []; foreach ($this->ensureMigrations as $migration) { $path = "Modules/Maintenance/Database/Migrations/{$migration}.php"; if (! file_exists(base_path($path))) { continue; } try { Artisan::call('migrate', [ '--path' => $path, '--force' => true, ]); $ran[] = $migration; } catch (\Throwable $e) { $errors[] = $migration.': '.$e->getMessage(); } } Artisan::call('optimize:clear'); $after = $this->assess(); return [ 'before' => $before, 'after' => $after, 'migrations_ran' => $ran, 'errors' => $errors, 'repaired' => $after['healthy'] || $after['score'] > $before['score'], ]; } protected function assessIntegrations(): array { return [ 'industrial_engineering' => class_exists(\Modules\IndustrialEngineering\Models\LineTemplate::class), 'asset_management' => class_exists(\Modules\AssetManagement\Entities\Asset::class), 'accounting' => class_exists(\Modules\Accounting\Entities\AccountingAccTransMapping::class), 'manufacturing' => class_exists(\Modules\Manufacturing\Entities\MfgRecipe::class) || class_exists(\Modules\Manufacturing\Http\Controllers\ProductionController::class), 'project' => Schema::hasTable('pjt_projects'), 'erp_products' => Schema::hasTable('products'), ]; } }