Add external import sources, work history, screening fields and Phase 1 docs
- New ExternalImportSource and ExternalWorkHistoryEntry models, controller and migrations - Screening fields on external profile imports - Admin and provider profile views, routes and workflow tests updated - Phase 1 validation, route inventory, gap report template and roadmap docs - Add the Google Sheets work-order parser tool (tools/google-sheets-wo-parser)
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('external_import_sources', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('provider_profile_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('external_profile_import_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->foreignId('uploaded_by_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('platform');
|
||||
$table->string('source_type');
|
||||
$table->string('parse_status')->default('pending');
|
||||
$table->string('review_status')->default('needs_review');
|
||||
$table->string('disk')->default('local');
|
||||
$table->string('path');
|
||||
$table->string('original_name');
|
||||
$table->string('mime_type')->nullable();
|
||||
$table->unsignedBigInteger('size')->default(0);
|
||||
$table->text('provider_notes')->nullable();
|
||||
$table->json('parsed_payload')->nullable();
|
||||
$table->json('parse_warnings')->nullable();
|
||||
$table->timestamp('uploaded_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['provider_profile_id', 'platform']);
|
||||
$table->index(['parse_status', 'review_status']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('external_import_sources');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('external_work_history_entries', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('provider_profile_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('external_profile_import_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('external_import_source_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('platform');
|
||||
$table->string('external_work_order_id')->nullable();
|
||||
$table->string('service_title');
|
||||
$table->date('performed_on')->nullable();
|
||||
$table->string('customer_name')->nullable();
|
||||
$table->string('work_type')->nullable();
|
||||
$table->string('location')->nullable();
|
||||
$table->decimal('rating', 3, 2)->nullable();
|
||||
$table->string('status')->nullable();
|
||||
$table->string('source_url')->nullable();
|
||||
$table->string('visibility')->default('private');
|
||||
$table->json('raw_payload')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['provider_profile_id', 'platform', 'performed_on']);
|
||||
$table->index(['external_profile_import_id', 'performed_on']);
|
||||
$table->unique(['external_profile_import_id', 'platform', 'external_work_order_id'], 'external_work_history_unique_order');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('external_work_history_entries');
|
||||
}
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('external_profile_imports', function (Blueprint $table) {
|
||||
$table->string('background_check_status')->nullable()->after('verification_status');
|
||||
$table->string('background_check_date')->nullable()->after('background_check_status');
|
||||
$table->string('drug_test_status')->nullable()->after('background_check_date');
|
||||
$table->string('drug_test_date')->nullable()->after('drug_test_status');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('external_profile_imports', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'background_check_status',
|
||||
'background_check_date',
|
||||
'drug_test_status',
|
||||
'drug_test_date',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -225,6 +225,109 @@ class DatabaseSeeder extends Seeder
|
||||
'evidence_notes' => 'Screenshots, chat, and deliverable notes would be attached here later.',
|
||||
]
|
||||
);
|
||||
|
||||
$this->seedValidationScenarios($buyer, $provider, $taxonomy);
|
||||
}
|
||||
|
||||
private function seedValidationScenarios(User $buyer, User $provider, array $taxonomy): void
|
||||
{
|
||||
$remoteJob = JobPost::updateOrCreate(
|
||||
['title' => 'Validation remote firewall rule review'],
|
||||
[
|
||||
'buyer_id' => $buyer->id,
|
||||
'status' => 'open',
|
||||
'service_category' => 'Networking',
|
||||
'work_category_id' => $taxonomy['categories']['server-networking']->id,
|
||||
'work_specialty_id' => $taxonomy['specialties']['networking']->id,
|
||||
'required_technician_level' => 4,
|
||||
'work_mode' => 'remote',
|
||||
'remote_eligible' => true,
|
||||
'pay_type' => 'hourly',
|
||||
'posted_terms_summary' => 'Provider hourly terms, one-hour minimum',
|
||||
'location' => 'Remote',
|
||||
'starts_at' => now()->addDays(3)->setTime(10, 0),
|
||||
'time_window' => 'Scheduled remote window',
|
||||
'schedule_type' => 'scheduled',
|
||||
'scope' => 'Review a planned firewall rule change and confirm the rollback path before the buyer applies it.',
|
||||
'primary_objective' => 'Validate a firewall rule change plan before production use.',
|
||||
'included_work' => 'Review requested ports, source/destination notes, rollback steps, and test plan.',
|
||||
'excluded_work' => 'Provider does not make production changes or retain credentials.',
|
||||
'maximum_onsite_expectations' => 'Remote advisory only.',
|
||||
'expected_duration' => 'One hour',
|
||||
'required_skills' => 'Firewall policy review, networking, change planning',
|
||||
'required_tools' => 'Video bridge and shared screen',
|
||||
'deliverables' => "Written risk notes\nRollback checklist\nGo/no-go recommendation",
|
||||
'closeout_conditions' => 'Buyer confirms the written recommendation is received.',
|
||||
'support_channel' => 'Video bridge',
|
||||
'support_expected_response_time' => 'Immediate during scheduled window',
|
||||
'support_availability_window' => 'Full scheduled window',
|
||||
'primary_contact_name' => 'Network Change Lead',
|
||||
'primary_contact_email' => 'buyer@example.com',
|
||||
'backup_contact_name' => 'Backup Dispatch',
|
||||
'backup_contact_email' => 'admin@example.com',
|
||||
'contact_certified' => true,
|
||||
'contact_certified_by_id' => $buyer->id,
|
||||
'contact_certified_at' => now(),
|
||||
'scope_clarity_status' => 'clear',
|
||||
'risk_flags' => [],
|
||||
'payment_terms' => 'Direct buyer/provider payment terms.',
|
||||
'vendor_onboarding' => 'No credential storage in platform.',
|
||||
'visibility' => 'public',
|
||||
]
|
||||
);
|
||||
|
||||
$remoteJob->comments()->firstOrCreate(
|
||||
['user_id' => $provider->id, 'body' => 'Validation note: this job should appear when remote-only and high-level networking filters are used.']
|
||||
);
|
||||
|
||||
$riskyJob = JobPost::updateOrCreate(
|
||||
['title' => 'Validation risky smart-hands dispatch'],
|
||||
[
|
||||
'buyer_id' => $buyer->id,
|
||||
'status' => 'open',
|
||||
'service_category' => 'Smart hands',
|
||||
'work_category_id' => $taxonomy['categories']['server-networking']->id,
|
||||
'work_specialty_id' => $taxonomy['specialties']['networking']->id,
|
||||
'required_technician_level' => 1,
|
||||
'work_mode' => 'onsite',
|
||||
'remote_eligible' => false,
|
||||
'pay_type' => 'not_listed',
|
||||
'posted_terms_summary' => 'Terms not listed for validation',
|
||||
'location' => 'Dallas, TX',
|
||||
'starts_at' => now()->addHours(12),
|
||||
'time_window' => 'ASAP',
|
||||
'schedule_type' => 'urgent',
|
||||
'scope' => 'Show up onsite and handle whatever network issue the store has.',
|
||||
'primary_objective' => 'Troubleshoot the network outage and fix any related issue.',
|
||||
'included_work' => null,
|
||||
'excluded_work' => null,
|
||||
'maximum_onsite_expectations' => null,
|
||||
'expected_duration' => null,
|
||||
'required_skills' => 'Troubleshooting, cabling, firewall, wireless',
|
||||
'required_tools' => null,
|
||||
'required_certifications' => 'Network certification preferred',
|
||||
'deliverables' => null,
|
||||
'closeout_conditions' => null,
|
||||
'supplemental_instructions' => str_repeat('Follow all onsite instructions. ', 90),
|
||||
'support_channel' => null,
|
||||
'support_expected_response_time' => null,
|
||||
'support_availability_window' => null,
|
||||
'primary_contact_name' => 'Store Manager',
|
||||
'primary_contact_email' => 'buyer@example.com',
|
||||
'backup_contact_name' => null,
|
||||
'backup_contact_email' => null,
|
||||
'contact_certified' => false,
|
||||
'contact_certified_by_id' => null,
|
||||
'contact_certified_at' => null,
|
||||
'payment_terms' => 'Direct buyer/provider payment terms.',
|
||||
'vendor_onboarding' => 'Buyer to advise after assignment.',
|
||||
'visibility' => 'public',
|
||||
]
|
||||
);
|
||||
|
||||
$riskyJob->risk_flags = $riskyJob->computeRiskFlags();
|
||||
$riskyJob->scope_clarity_status = $riskyJob->computedScopeClarityStatus();
|
||||
$riskyJob->save();
|
||||
}
|
||||
|
||||
private function seedTaxonomy(): array
|
||||
|
||||
Reference in New Issue
Block a user