> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Braian551/viax/llms.txt
> Use this file to discover all available pages before exploring further.

# Document Verification

> Upload and verify driver license, vehicle documents, SOAT, and Tecnomecánica

## Document Upload System

Viax requires drivers to upload photos of official documents for verification. All uploads are secure, organized by driver, and reviewed by our admin team.

<Info>
  Documents are stored securely on the server with automatic cleanup of outdated files. Only the latest version is kept active.
</Info>

## Required Documents

<CardGroup cols={2}>
  <Card title="Driver's License" icon="id-card" color="#3b82f6">
    Front and back photos of your valid driver's license
  </Card>

  <Card title="SOAT Insurance" icon="shield" color="#10b981">
    Mandatory vehicle insurance certificate photo
  </Card>

  <Card title="Tecnomecánica" icon="wrench" color="#f59e0b">
    Technical inspection certificate photo
  </Card>

  <Card title="Vehicle Ownership" icon="file-lines" color="#8b5cf6">
    Tarjeta de Propiedad (ownership card) photo
  </Card>
</CardGroup>

## Upload Process

### Step 1: Document Selection

The app provides an easy interface to select photos:

```dart Photo Upload Widget theme={null}
Widget _buildPhotoUpload({
  required String label,
  required String? photoPath,
  required VoidCallback onTap,
}) {
  final hasPhoto = photoPath != null;
  
  return GestureDetector(
    onTap: onTap,
    child: Container(
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: hasPhoto ? Colors.green.withOpacity(0.1) : Colors.grey[100],
        borderRadius: BorderRadius.circular(12),
        border: Border.all(
          color: hasPhoto ? Colors.green : Colors.grey[300],
          width: 2,
        ),
      ),
      child: Row(
        children: [
          Icon(
            hasPhoto ? Icons.check_circle : Icons.camera_alt,
            color: hasPhoto ? Colors.green : Colors.grey[600],
          ),
          SizedBox(width: 12),
          Expanded(
            child: Text(
              label,
              style: TextStyle(
                fontSize: 16,
                fontWeight: hasPhoto ? FontWeight.bold : FontWeight.normal,
              ),
            ),
          ),
          if (hasPhoto)
            Text('Selected ✓', style: TextStyle(color: Colors.green)),
        ],
      ),
    ),
  );
}
```

### Step 2: Image Selection Method

Choose between camera or gallery:

<Tabs>
  <Tab title="Camera">
    Take a photo directly with your phone's camera

    ```dart Take Photo theme={null}
    Future<void> _pickImageFromCamera(String documentType) async {
      final ImagePicker picker = ImagePicker();
      
      final XFile? image = await picker.pickImage(
        source: ImageSource.camera,
        maxWidth: 1920,
        maxHeight: 1920,
        imageQuality: 85,
      );
      
      if (image != null) {
        setState(() {
          switch(documentType) {
            case 'soat':
              _soatFotoPath = image.path;
              break;
            case 'tecnomecanica':
              _tecnomecanicaFotoPath = image.path;
              break;
            case 'tarjeta_propiedad':
              _tarjetaPropiedadFotoPath = image.path;
              break;
          }
        });
      }
    }
    ```
  </Tab>

  <Tab title="Gallery">
    Select an existing photo from your gallery

    ```dart Select from Gallery theme={null}
    Future<void> _pickImageFromGallery(String documentType) async {
      final ImagePicker picker = ImagePicker();
      
      final XFile? image = await picker.pickImage(
        source: ImageSource.gallery,
        maxWidth: 1920,
        maxHeight: 1920,
        imageQuality: 85,
      );
      
      if (image != null) {
        // Update state with selected image path
        _updateDocumentPath(documentType, image.path);
      }
    }
    ```
  </Tab>
</Tabs>

<Warning>
  **Image Quality Requirements:**

  * Maximum size: 5MB per photo
  * Recommended resolution: 1920x1920 pixels
  * Automatic compression to 85% quality
  * Supported formats: JPG, PNG, WEBP, PDF
</Warning>

### Step 3: Document Upload Service

The `DocumentUploadService` handles secure uploads:

```dart Document Upload Service theme={null}
class DocumentUploadService {
  static const String baseUrl = 'http://76.13.114.194';
  static const String uploadEndpoint = '$baseUrl/conductor/upload_documents.php';
  
  /// Upload a single document
  static Future<String?> uploadDocument({
    required int conductorId,
    required String tipoDocumento,
    required String imagePath,
  }) async {
    try {
      var request = http.MultipartRequest('POST', Uri.parse(uploadEndpoint));
      
      // Add form fields
      request.fields['conductor_id'] = conductorId.toString();
      request.fields['tipo_documento'] = tipoDocumento;
      
      // Add file
      var file = await http.MultipartFile.fromPath(
        'documento',
        imagePath,
        contentType: MediaType('image', 'jpeg'),
      );
      request.files.add(file);
      
      // Send request
      var response = await request.send();
      var responseData = await response.stream.bytesToString();
      var jsonResponse = json.decode(responseData);
      
      if (jsonResponse['success'] == true) {
        return jsonResponse['data']['url'];
      } else {
        throw Exception(jsonResponse['message'] ?? 'Upload failed');
      }
    } catch (e) {
      print('Upload error: $e');
      return null;
    }
  }
  
  /// Upload multiple documents at once
  static Future<Map<String, String?>> uploadMultipleDocuments({
    required int conductorId,
    required Map<String, String> documents,
  }) async {
    Map<String, String?> results = {};
    
    for (var entry in documents.entries) {
      final url = await uploadDocument(
        conductorId: conductorId,
        tipoDocumento: entry.key,
        imagePath: entry.value,
      );
      results[entry.key] = url;
    }
    
    return results;
  }
}
```

## Document Types & Validation

### Driver's License

<ParamField path="licencia" type="DriverLicense" required>
  Driver's license with front and back photos
</ParamField>

```dart License Upload theme={null}
// Upload license photo
final licenseUrl = await DocumentUploadService.uploadDocument(
  conductorId: conductorId,
  tipoDocumento: 'licencia',
  imagePath: licenseFotoPath,
);

// Update license in profile
final license = DriverLicense(
  numero: licenseNumber,
  categoria: LicenseCategory.c1,
  fechaExpedicion: issueDate,
  fechaVencimiento: expiryDate,
  foto: licenseUrl,
  isVerified: false, // Will be verified by admin
);
```

**Validation checks:**

* ✅ License number is not empty
* ✅ Category is C1, C2, or C3 (public service)
* ✅ Expiry date is in the future
* ✅ Not expiring within 30 days

### SOAT (Vehicle Insurance)

<ParamField path="soat" type="object" required>
  Mandatory vehicle insurance certificate
</ParamField>

```dart Vehicle Model - SOAT theme={null}
class VehicleModel {
  final String? soatNumero;
  final DateTime? soatVencimiento;
  final String? fotoSoat;
  
  bool get isSoatValid {
    if (soatVencimiento == null) return false;
    return soatVencimiento!.isAfter(DateTime.now());
  }
  
  bool get isSoatExpiringSoon {
    if (soatVencimiento == null) return false;
    final daysUntilExpiry = soatVencimiento!.difference(DateTime.now()).inDays;
    return daysUntilExpiry <= 30 && daysUntilExpiry > 0;
  }
}
```

**Validation checks:**

* ✅ SOAT number provided
* ✅ Expiry date is valid and in the future
* ✅ Photo uploaded and visible

### Tecnomecánica (Technical Inspection)

<ParamField path="tecnomecanica" type="object" required>
  Vehicle technical inspection certificate
</ParamField>

```dart Tecnomecánica Validation theme={null}
final String? tecnomecanicaNumero;
final DateTime? tecnomecanicaVencimiento;
final String? fotoTecnomecanica;

bool get isTecnomecanicaValid {
  return tecnomecanicaNumero != null &&
         tecnomecanicaNumero!.isNotEmpty &&
         tecnomecanicaVencimiento != null &&
         tecnomecanicaVencimiento!.isAfter(DateTime.now());
}
```

### Tarjeta de Propiedad (Ownership Card)

<ParamField path="tarjeta_propiedad" type="object" required>
  Vehicle ownership registration card
</ParamField>

```dart Ownership Card theme={null}
final String? tarjetaPropiedadNumero;
final String? fotoTarjetaPropiedad;

bool get isOwnershipComplete {
  return tarjetaPropiedadNumero != null &&
         tarjetaPropiedadNumero!.isNotEmpty &&
         fotoTarjetaPropiedad != null &&
         fotoTarjetaPropiedad!.isNotEmpty;
}
```

## Upload Workflow Example

Complete example of uploading vehicle documents:

<Steps>
  <Step title="Collect Document Paths">
    User selects all required photos

    ```dart theme={null}
    String? _soatFotoPath;
    String? _tecnomecanicaFotoPath;
    String? _tarjetaPropiedadFotoPath;
    ```
  </Step>

  <Step title="Upload Documents">
    Upload all documents before saving vehicle

    ```dart Upload Documents theme={null}
    final uploadResults = await provider.uploadVehicleDocuments(
      conductorId: conductorId,
      soatFotoPath: _soatFotoPath,
      tecnomecanicaFotoPath: _tecnomecanicaFotoPath,
      tarjetaPropiedadFotoPath: _tarjetaPropiedadFotoPath,
    );

    if (uploadResults.containsValue(null)) {
      // Some uploads failed
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Some documents failed to upload'))
      );
      return;
    }
    ```
  </Step>

  <Step title="Save Vehicle with URLs">
    Create vehicle with uploaded document URLs

    ```dart Save Vehicle theme={null}
    final vehicle = VehicleModel(
      placa: plateNumber,
      tipo: VehicleType.auto,
      marca: brand,
      modelo: model,
      anio: year,
      color: color,
      soatNumero: soatNumber,
      soatVencimiento: soatExpiry,
      fotoSoat: uploadResults['soat'],
      tecnomecanicaNumero: techNumber,
      tecnomecanicaVencimiento: techExpiry,
      fotoTecnomecanica: uploadResults['tecnomecanica'],
      tarjetaPropiedadNumero: ownershipNumber,
      fotoTarjetaPropiedad: uploadResults['tarjeta_propiedad'],
    );

    await provider.updateVehicle(
      conductorId: conductorId,
      vehicle: vehicle,
    );
    ```
  </Step>

  <Step title="Show Success">
    Display confirmation message

    ```dart theme={null}
    if (success) {
      showDialog(
        context: context,
        builder: (context) => ApprovalSuccessDialog(
          message: 'Vehicle registered successfully!'
        ),
      );
    }
    ```
  </Step>
</Steps>

## Server-Side Processing

### File Storage Structure

Documents are organized on the server:

```bash theme={null}
viax/backend/uploads/documentos/
├── conductor_1/
│   ├── soat_1730000000_a1b2c3d4.jpg
│   ├── tecnomecanica_1730000000_e5f6g7h8.jpg
│   └── tarjeta_propiedad_1730000000_i9j0k1l2.jpg
├── conductor_7/
│   ├── licencia_1730000000_m3n4o5p6.jpg
│   └── soat_1730000000_q7r8s9t0.jpg
└── conductor_15/
    └── ...
```

### Upload Endpoint Response

```json theme={null}
{
  "success": true,
  "message": "Document uploaded successfully",
  "data": {
    "tipo_documento": "soat",
    "url": "uploads/documentos/conductor_7/soat_1730000000_a1b2c3d4.jpg",
    "conductor_id": 7,
    "fecha_subida": "2025-10-25 15:30:00"
  }
}
```

## Security & Privacy

<AccordionGroup>
  <Accordion title="File Validation" icon="shield-check">
    Server performs strict validation:

    * File type verification (MIME type check)
    * Size limit enforcement (max 5MB)
    * Only allowed formats: JPG, PNG, WEBP, PDF
    * Conductor existence verification
  </Accordion>

  <Accordion title="Secure Storage" icon="lock">
    * Files stored outside web root when possible
    * Unique filenames prevent overwrites
    * `.htaccess` protection against PHP execution
    * Automatic cleanup of old documents
  </Accordion>

  <Accordion title="Access Control" icon="user-shield">
    * Only authenticated conductors can upload
    * Conductors can only access their own documents
    * Admin approval required before activation
  </Accordion>
</AccordionGroup>

## Document History

The system maintains a history of all uploaded documents:

```sql theme={null}
CREATE TABLE documentos_conductor_historial (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  conductor_id BIGINT UNSIGNED NOT NULL,
  tipo_documento VARCHAR(50) NOT NULL,
  url_documento VARCHAR(500) NOT NULL,
  activo TINYINT(1) DEFAULT 1,
  fecha_subida TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  reemplazado_en TIMESTAMP NULL,
  FOREIGN KEY (conductor_id) REFERENCES usuarios(id)
);
```

* When a new document is uploaded, the old one is marked as `activo = 0`
* The `reemplazado_en` timestamp is set
* Old files are deleted from the server

## Troubleshooting

<AccordionGroup>
  <Accordion title="Upload Fails" icon="circle-xmark">
    **Possible causes:**

    * File too large (over 5MB)
    * Invalid file format
    * Network connection issue
    * Server permissions problem

    **Solution:**

    * Reduce image quality/size
    * Use JPG format
    * Check internet connection
    * Try again later
  </Accordion>

  <Accordion title="Document Rejected" icon="ban">
    **Common reasons:**

    * Photo is blurry or unreadable
    * Document is expired
    * Information doesn't match profile
    * Wrong document uploaded

    **Solution:**

    * Take a clear, well-lit photo
    * Ensure all text is readable
    * Verify dates are valid
    * Upload the correct document
  </Accordion>
</AccordionGroup>

<Tip>
  **Best Practices:**

  * Take photos in good lighting
  * Ensure all corners of the document are visible
  * Avoid shadows and glare
  * Use a flat surface as background
  * Verify all text is readable before uploading
</Tip>

## Next Steps

<CardGroup cols={2}>
  <Card title="Vehicle Management" icon="car" href="/drivers/vehicle-management">
    Complete vehicle registration and details
  </Card>

  <Card title="Profile Management" icon="user" href="/drivers/profile-management">
    Update your driver profile settings
  </Card>
</CardGroup>
