Files
EM2_ERP/em2rp/lib/services/ics_export_service.dart
2025-12-20 15:56:57 +01:00

268 lines
7.9 KiB
Dart

import 'package:em2rp/models/event_model.dart';
import 'package:intl/intl.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class IcsExportService {
/// Génère un fichier ICS à partir d'un événement
static Future<String> generateIcsContent(EventModel event) async {
final now = DateTime.now().toUtc();
final timestamp = DateFormat('yyyyMMddTHHmmss').format(now) + 'Z';
// Récupérer les informations supplémentaires
final eventTypeName = await _getEventTypeName(event.eventTypeId);
final workforce = await _getWorkforceDetails(event.workforce);
final optionsWithNames = await _getOptionsDetails(event.options);
// Formater les dates au format ICS (UTC)
final startDate = _formatDateForIcs(event.startDateTime);
final endDate = _formatDateForIcs(event.endDateTime);
// Construire la description détaillée
final description = _buildDescription(event, eventTypeName, workforce, optionsWithNames);
// Générer un UID unique basé sur l'ID de l'événement
final uid = 'em2rp-${event.id}@em2rp.app';
// Construire le contenu ICS
final icsContent = '''BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//EM2RP//Event Manager//FR
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
UID:$uid
DTSTAMP:$timestamp
DTSTART:$startDate
DTEND:$endDate
SUMMARY:${_escapeIcsText(event.name)}
DESCRIPTION:${_escapeIcsText(description)}
LOCATION:${_escapeIcsText(event.address)}
STATUS:${_getEventStatus(event.status)}
CATEGORIES:${_escapeIcsText(eventTypeName)}
END:VEVENT
END:VCALENDAR''';
return icsContent;
}
/// Récupère le nom du type d'événement
static Future<String> _getEventTypeName(String eventTypeId) async {
if (eventTypeId.isEmpty) return 'Non spécifié';
try {
final doc = await FirebaseFirestore.instance
.collection('eventTypes')
.doc(eventTypeId)
.get();
if (doc.exists) {
return doc.data()?['name'] as String? ?? eventTypeId;
}
} catch (e) {
print('Erreur lors de la récupération du type d\'événement: $e');
}
return eventTypeId;
}
/// Récupère les détails de la main d'œuvre
static Future<List<String>> _getWorkforceDetails(List<DocumentReference> workforce) async {
final List<String> workforceNames = [];
for (final ref in workforce) {
try {
final doc = await ref.get();
if (doc.exists) {
final data = doc.data() as Map<String, dynamic>?;
if (data != null) {
final firstName = data['firstName'] ?? '';
final lastName = data['lastName'] ?? '';
if (firstName.isNotEmpty || lastName.isNotEmpty) {
workforceNames.add('$firstName $lastName'.trim());
}
}
}
} catch (e) {
print('Erreur lors de la récupération des détails utilisateur: $e');
}
}
return workforceNames;
}
/// Récupère les détails des options
static Future<List<Map<String, dynamic>>> _getOptionsDetails(List<Map<String, dynamic>> options) async {
final List<Map<String, dynamic>> optionsWithNames = [];
for (final option in options) {
try {
final optionId = option['id'] ?? option['optionId'];
if (optionId == null || optionId.toString().isEmpty) {
// Si pas d'ID, garder le nom tel quel
optionsWithNames.add({
'name': option['name'] ?? 'Option inconnue',
'quantity': option['quantity'],
});
continue;
}
// Récupérer le nom depuis Firestore
final doc = await FirebaseFirestore.instance
.collection('options')
.doc(optionId.toString())
.get();
if (doc.exists) {
final data = doc.data();
optionsWithNames.add({
'name': data?['name'] ?? option['name'] ?? 'Option inconnue',
'quantity': option['quantity'],
});
} else {
// Document n'existe pas, garder le nom de l'option
optionsWithNames.add({
'name': option['name'] ?? 'Option inconnue',
'quantity': option['quantity'],
});
}
} catch (e) {
print('Erreur lors de la récupération des détails option: $e');
optionsWithNames.add({
'name': option['name'] ?? 'Option inconnue',
'quantity': option['quantity'],
});
}
}
return optionsWithNames;
}
/// Construit la description détaillée de l'événement
static String _buildDescription(
EventModel event,
String eventTypeName,
List<String> workforce,
List<Map<String, dynamic>> optionsWithNames,
) {
final buffer = StringBuffer();
// Type d'événement
buffer.writeln('TYPE: $eventTypeName');
buffer.writeln('');
// Description
if (event.description.isNotEmpty) {
buffer.writeln('DESCRIPTION:');
buffer.writeln(event.description);
buffer.writeln('');
}
// Jauge
if (event.jauge != null) {
buffer.writeln('JAUGE: ${event.jauge} personnes');
}
// Contact email
if (event.contactEmail != null && event.contactEmail!.isNotEmpty) {
buffer.writeln('EMAIL DE CONTACT: ${event.contactEmail}');
}
// Contact téléphone
if (event.contactPhone != null && event.contactPhone!.isNotEmpty) {
buffer.writeln('TÉLÉPHONE DE CONTACT: ${event.contactPhone}');
}
// Adresse
if (event.address.isNotEmpty) {
buffer.writeln('');
buffer.writeln('ADRESSE: ${event.address}');
}
// Temps d'installation et démontage
if (event.installationTime > 0 || event.disassemblyTime > 0) {
buffer.writeln('');
if (event.installationTime > 0) {
buffer.writeln('INSTALLATION: ${event.installationTime}h');
}
if (event.disassemblyTime > 0) {
buffer.writeln('DÉMONTAGE: ${event.disassemblyTime}h');
}
}
// Main d'œuvre
if (workforce.isNotEmpty) {
buffer.writeln('');
buffer.writeln('MAIN D\'ŒUVRE:');
for (final name in workforce) {
buffer.writeln(' - $name');
}
}
// Options
if (optionsWithNames.isNotEmpty) {
buffer.writeln('');
buffer.writeln('OPTIONS:');
for (final option in optionsWithNames) {
final optionName = option['name'] ?? 'Option inconnue';
final quantity = option['quantity'];
if (quantity != null && quantity > 1) {
buffer.writeln(' - $optionName (x$quantity)');
} else {
buffer.writeln(' - $optionName');
}
}
}
// Prix
if (event.basePrice > 0) {
buffer.writeln('');
buffer.writeln('PRIX DE BASE: ${event.basePrice.toStringAsFixed(2)}');
}
// Lien vers l'application
buffer.writeln('');
buffer.writeln('---');
buffer.writeln('Géré par EM2RP Event Manager');
return buffer.toString();
}
/// Formate une date au format ICS (yyyyMMddTHHmmssZ)
static String _formatDateForIcs(DateTime dateTime) {
final utcDate = dateTime.toUtc();
return DateFormat('yyyyMMddTHHmmss').format(utcDate) + 'Z';
}
/// Échappe les caractères spéciaux pour le format ICS
static String _escapeIcsText(String text) {
return text
.replaceAll('\\', '\\\\')
.replaceAll(',', '\\,')
.replaceAll(';', '\\;')
.replaceAll('\n', '\\n')
.replaceAll('\r', '');
}
/// Convertit le statut de l'événement en statut ICS
static String _getEventStatus(EventStatus status) {
switch (status) {
case EventStatus.confirmed:
return 'CONFIRMED';
case EventStatus.canceled:
return 'CANCELLED';
case EventStatus.waitingForApproval:
return 'TENTATIVE';
}
}
/// Génère le nom du fichier ICS
static String generateFileName(EventModel event) {
final safeName = event.name
.replaceAll(RegExp(r'[^\w\s-]'), '')
.replaceAll(RegExp(r'\s+'), '_');
final dateStr = DateFormat('yyyyMMdd').format(event.startDateTime);
return 'event_${safeName}_$dateStr.ics';
}
}