299 lines
11 KiB
PHP
299 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\JobCard;
|
|
use App\Models\Estimate;
|
|
use App\Models\WorkOrder;
|
|
use App\Models\Diagnosis;
|
|
use App\Models\VehicleInspection;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class WorkflowService
|
|
{
|
|
public function __construct(
|
|
private NotificationService $notificationService
|
|
) {}
|
|
|
|
/**
|
|
* Create job card when vehicle arrives
|
|
*/
|
|
public function createJobCard(array $data): JobCard
|
|
{
|
|
return DB::transaction(function () use ($data) {
|
|
$jobCard = JobCard::create([
|
|
'customer_id' => $data['customer_id'],
|
|
'vehicle_id' => $data['vehicle_id'],
|
|
'service_advisor_id' => $data['service_advisor_id'],
|
|
'branch_code' => $data['branch_code'] ?? config('app.default_branch_code', 'ACC'),
|
|
'arrival_datetime' => $data['arrival_datetime'],
|
|
'mileage_in' => $data['mileage_in'],
|
|
'fuel_level_in' => $data['fuel_level_in'],
|
|
'customer_reported_issues' => $data['customer_reported_issues'],
|
|
'vehicle_condition_notes' => $data['vehicle_condition_notes'],
|
|
'keys_location' => $data['keys_location'],
|
|
'personal_items_removed' => $data['personal_items_removed'] ?? false,
|
|
'photos_taken' => $data['photos_taken'] ?? false,
|
|
'expected_completion_date' => $data['expected_completion_date'] ?? null,
|
|
'priority' => $data['priority'] ?? 'medium',
|
|
'notes' => $data['notes'] ?? null,
|
|
]);
|
|
|
|
// Create incoming inspection checklist
|
|
if (isset($data['inspection_checklist'])) {
|
|
VehicleInspection::create([
|
|
'job_card_id' => $jobCard->id,
|
|
'vehicle_id' => $jobCard->vehicle_id,
|
|
'inspector_id' => $data['inspector_id'],
|
|
'inspection_type' => 'incoming',
|
|
'current_mileage' => $data['mileage_in'],
|
|
'fuel_level' => $data['fuel_level_in'],
|
|
'inspection_checklist' => $data['inspection_checklist'],
|
|
'photos' => $data['inspection_photos'] ?? [],
|
|
'overall_condition' => $data['overall_condition'],
|
|
'inspection_date' => now(),
|
|
'notes' => $data['inspection_notes'] ?? null,
|
|
]);
|
|
}
|
|
|
|
return $jobCard;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Assign job card to service coordinator and start diagnosis
|
|
*/
|
|
public function assignToServiceCoordinator(JobCard $jobCard, int $serviceCoordinatorId): Diagnosis
|
|
{
|
|
$diagnosis = Diagnosis::create([
|
|
'job_card_id' => $jobCard->id,
|
|
'service_coordinator_id' => $serviceCoordinatorId,
|
|
'customer_reported_issues' => $jobCard->customer_reported_issues,
|
|
'diagnosis_status' => 'in_progress',
|
|
'diagnosis_date' => now(),
|
|
]);
|
|
|
|
$jobCard->update(['status' => 'in_diagnosis']);
|
|
|
|
return $diagnosis;
|
|
}
|
|
|
|
/**
|
|
* Complete diagnosis and create estimate
|
|
*/
|
|
public function completeDiagnosis(Diagnosis $diagnosis, array $diagnosisData, array $estimateItems): Estimate
|
|
{
|
|
return DB::transaction(function () use ($diagnosis, $diagnosisData, $estimateItems) {
|
|
// Update diagnosis
|
|
$diagnosis->update([
|
|
'diagnostic_findings' => $diagnosisData['diagnostic_findings'],
|
|
'root_cause_analysis' => $diagnosisData['root_cause_analysis'],
|
|
'recommended_repairs' => $diagnosisData['recommended_repairs'],
|
|
'additional_issues_found' => $diagnosisData['additional_issues_found'] ?? null,
|
|
'priority_level' => $diagnosisData['priority_level'] ?? 'medium',
|
|
'estimated_repair_time' => $diagnosisData['estimated_repair_time'],
|
|
'parts_required' => $diagnosisData['parts_required'] ?? [],
|
|
'labor_operations' => $diagnosisData['labor_operations'] ?? [],
|
|
'special_tools_required' => $diagnosisData['special_tools_required'] ?? [],
|
|
'safety_concerns' => $diagnosisData['safety_concerns'] ?? null,
|
|
'customer_authorization_required' => $diagnosisData['customer_authorization_required'] ?? false,
|
|
'diagnosis_status' => 'completed',
|
|
'notes' => $diagnosisData['notes'] ?? null,
|
|
]);
|
|
|
|
// Create estimate
|
|
$estimate = Estimate::create([
|
|
'job_card_id' => $diagnosis->job_card_id,
|
|
'diagnosis_id' => $diagnosis->id,
|
|
'prepared_by_id' => $diagnosis->service_coordinator_id,
|
|
'tax_rate' => config('app.default_tax_rate', 8.25),
|
|
'validity_period_days' => 30,
|
|
'terms_and_conditions' => config('app.default_estimate_terms'),
|
|
'status' => 'draft',
|
|
]);
|
|
|
|
// Add estimate line items
|
|
foreach ($estimateItems as $item) {
|
|
$estimate->lineItems()->create($item);
|
|
}
|
|
|
|
// Calculate totals
|
|
$estimate->calculateTotals();
|
|
|
|
// Update job card status
|
|
$diagnosis->jobCard->update(['status' => 'estimate_sent']);
|
|
|
|
return $estimate;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Send estimate to customer
|
|
*/
|
|
public function sendEstimateToCustomer(Estimate $estimate): void
|
|
{
|
|
$this->notificationService->sendEstimateToCustomer($estimate);
|
|
}
|
|
|
|
/**
|
|
* Approve estimate and create work order
|
|
*/
|
|
public function approveEstimate(Estimate $estimate, string $approvalMethod = 'portal'): WorkOrder
|
|
{
|
|
return DB::transaction(function () use ($estimate, $approvalMethod) {
|
|
// Update estimate
|
|
$estimate->update([
|
|
'customer_approval_status' => 'approved',
|
|
'customer_approved_at' => now(),
|
|
'customer_approval_method' => $approvalMethod,
|
|
'status' => 'approved',
|
|
]);
|
|
|
|
// Update job card
|
|
$estimate->jobCard->update(['status' => 'approved']);
|
|
|
|
// Send notifications
|
|
$this->notificationService->notifyEstimateApproved($estimate);
|
|
|
|
// Create work order
|
|
$workOrder = WorkOrder::create([
|
|
'job_card_id' => $estimate->job_card_id,
|
|
'estimate_id' => $estimate->id,
|
|
'service_coordinator_id' => $estimate->diagnosis->service_coordinator_id,
|
|
'priority' => $estimate->jobCard->priority,
|
|
'work_description' => $estimate->diagnosis->recommended_repairs,
|
|
'special_instructions' => $estimate->diagnosis->notes,
|
|
'quality_check_required' => true,
|
|
'status' => 'pending',
|
|
]);
|
|
|
|
return $workOrder;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Assign work order to technician and start work
|
|
*/
|
|
public function assignWorkOrder(WorkOrder $workOrder, int $technicianId): void
|
|
{
|
|
$workOrder->update([
|
|
'assigned_technician_id' => $technicianId,
|
|
'status' => 'assigned',
|
|
'actual_start_time' => now(),
|
|
]);
|
|
|
|
$workOrder->jobCard->update(['status' => 'in_progress']);
|
|
}
|
|
|
|
/**
|
|
* Complete work order and perform quality check
|
|
*/
|
|
public function completeWorkOrder(WorkOrder $workOrder, int $qualityCheckerId): void
|
|
{
|
|
$workOrder->update([
|
|
'status' => 'quality_check',
|
|
'actual_completion_time' => now(),
|
|
'quality_checked_by_id' => $qualityCheckerId,
|
|
'quality_check_date' => now(),
|
|
'completion_percentage' => 100,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Perform outgoing inspection and final quality check
|
|
*/
|
|
public function performOutgoingInspection(JobCard $jobCard, array $inspectionData, int $inspectorId): void
|
|
{
|
|
// Create outgoing inspection
|
|
$outgoingInspection = VehicleInspection::create([
|
|
'job_card_id' => $jobCard->id,
|
|
'vehicle_id' => $jobCard->vehicle_id,
|
|
'inspector_id' => $inspectorId,
|
|
'inspection_type' => 'outgoing',
|
|
'current_mileage' => $inspectionData['mileage_out'],
|
|
'fuel_level' => $inspectionData['fuel_level_out'],
|
|
'inspection_checklist' => $inspectionData['inspection_checklist'],
|
|
'photos' => $inspectionData['photos'] ?? [],
|
|
'overall_condition' => $inspectionData['overall_condition'],
|
|
'inspection_date' => now(),
|
|
'notes' => $inspectionData['notes'] ?? null,
|
|
]);
|
|
|
|
// Compare with incoming inspection
|
|
$incomingInspection = $jobCard->incomingInspection;
|
|
if ($incomingInspection) {
|
|
$discrepancies = $outgoingInspection->compareWithOtherInspection($incomingInspection);
|
|
|
|
if (!empty($discrepancies)) {
|
|
// Alert service supervisor about discrepancies
|
|
$this->notificationService->sendQualityAlert($jobCard, $discrepancies);
|
|
|
|
$outgoingInspection->update([
|
|
'discrepancies_found' => $discrepancies,
|
|
'follow_up_required' => true,
|
|
]);
|
|
} else {
|
|
// No discrepancies, mark as completed
|
|
$jobCard->update([
|
|
'status' => 'completed',
|
|
'mileage_out' => $inspectionData['mileage_out'],
|
|
'fuel_level_out' => $inspectionData['fuel_level_out'],
|
|
'completion_datetime' => now(),
|
|
]);
|
|
|
|
// Notify customer
|
|
$this->notificationService->notifyVehicleReady($jobCard);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close job card after delivery
|
|
*/
|
|
public function closeJobCard(JobCard $jobCard, array $deliveryData): void
|
|
{
|
|
$jobCard->update([
|
|
'status' => 'delivered',
|
|
'delivery_method' => $deliveryData['delivery_method'],
|
|
'customer_satisfaction_rating' => $deliveryData['satisfaction_rating'] ?? null,
|
|
'completion_datetime' => now(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get workflow status for a job card
|
|
*/
|
|
public function getWorkflowStatus(JobCard $jobCard): array
|
|
{
|
|
return [
|
|
'job_card' => $jobCard,
|
|
'incoming_inspection' => $jobCard->incomingInspection,
|
|
'diagnosis' => $jobCard->diagnosis,
|
|
'estimate' => $jobCard->estimates()->latest()->first(),
|
|
'work_orders' => $jobCard->workOrders,
|
|
'outgoing_inspection' => $jobCard->outgoingInspection,
|
|
'completion_percentage' => $this->calculateCompletionPercentage($jobCard),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Calculate completion percentage for a job card
|
|
*/
|
|
private function calculateCompletionPercentage(JobCard $jobCard): int
|
|
{
|
|
$steps = [
|
|
'received' => 10,
|
|
'in_diagnosis' => 25,
|
|
'estimate_sent' => 40,
|
|
'approved' => 50,
|
|
'in_progress' => 75,
|
|
'quality_check' => 90,
|
|
'completed' => 95,
|
|
'delivered' => 100,
|
|
];
|
|
|
|
return $steps[$jobCard->status] ?? 0;
|
|
}
|
|
}
|