import 'package:equatable/equatable.dart'; import 'package:flux/core/utils/extensions.dart'; import 'package:flux/features/customers/models/customer_model.dart'; /// Enum per il tipo di ticket enum TicketType { repair('repair', 'Riparazione'), softwareSetup('software_setup', 'Impost. software'), dataTransfer('data_transfer', 'Trasf. dati'), operationTicket('operation_ticket', 'Ticket operazione'), other('other', 'Altro'); final String value; final String displayValue; const TicketType(this.value, this.displayValue); static TicketType fromString(String val) { return TicketType.values.firstWhere( (e) => e.value == val, orElse: () => TicketType.other, ); } } /// Enum per lo stato del ticket enum TicketStatus { open('open', 'Aperto'), inProgress('in_progress', 'In corso'), waitingForParts('waiting_for_parts', 'In attesa di ricambi'), ready('ready', 'Pronto'), closed('closed', 'Chiuso'), waitingForShipping('waiting_for_shipping', 'In attesa di spedire'), waitingForReturn('waiting_for_return', 'In attesa di ritorno'), waitingForQuote('waiting_for_quote', 'In attesa di preventivo'), waitingForCustomer('waiting_for_customer', 'In attesa del cliente'); final String value; final String displayValue; const TicketStatus(this.value, this.displayValue); static TicketStatus fromString(String? val) { return TicketStatus.values.firstWhere( (e) => e.value == val, orElse: () => TicketStatus.open, ); } } /// Enum per il risultato del ticket (OK / KO) enum TicketResult { success('success', 'Risolto (OK)'), failure('failure', 'Non Risolto (KO)'); final String value; final String displayValue; const TicketResult(this.value, this.displayValue); static TicketResult? fromString(String? val) { if (val == null) return null; return TicketResult.values.firstWhere( (e) => e.value == val, orElse: () => TicketResult.success, ); } } /// Enum per il tipo di garanzia enum WarrantyType { manufacturerWarranty('manufacturer_warranty', 'Garanzia produttore'), providerWarranty('provider_warranty', 'Garanzia gestore'), internalWarranty('internal_warranty', 'Garanzia interna'), noWarranty('no_warranty', 'Fuori garanzia'); final String value; final String displayValue; const WarrantyType(this.value, this.displayValue); static WarrantyType? fromString(String? val) { return WarrantyType.values.firstWhere( (e) => e.value == val, orElse: () => WarrantyType.noWarranty, ); } } class TicketModel extends Equatable { final String? id; // Null se non ancora salvato final DateTime? createdAt; final String companyId; final String? storeId; final String? customerId; final String? targetModelId; final String? targetSn; final String? targetPassword; final String? sourceModelId; final String? sourceSn; final String? sourcePassword; final double customerPrice; final double internalCost; final DateTime? closedAt; final DateTime? returnedAt; final String request; final WarrantyType? warrantyType; final String? publicNotes; final String? internalNotes; final String? referenceId; final String? alternativePhoneNumber; final bool hasCourtesyDevice; final TicketType ticketType; final TicketStatus ticketStatus; final DateTime? estimatedDeliveryAt; final TicketResult? ticketResult; final String? resolutionNotes; final CustomerModel? customer; final String? targetModelName; final String? sourceModelName; final String? createdById; final String? createdByName; final String? assignedToId; final String? assignedToName; final String? includedAccessories; const TicketModel({ this.id, this.createdAt, required this.companyId, this.storeId, this.customerId, this.targetModelId, this.targetSn, this.targetPassword, this.sourceModelId, this.sourceSn, this.sourcePassword, this.customerPrice = 0.0, this.internalCost = 0.0, this.closedAt, this.returnedAt, this.request = '', this.warrantyType, this.publicNotes, this.internalNotes, this.referenceId, this.alternativePhoneNumber, this.hasCourtesyDevice = false, required this.ticketType, this.ticketStatus = TicketStatus.closed, this.estimatedDeliveryAt, this.ticketResult, this.resolutionNotes, this.customer, this.targetModelName, this.sourceModelName, this.createdById, this.createdByName, this.assignedToId, this.assignedToName, this.includedAccessories, }); /// Factory per creare un ticket vuoto (utile per i form di creazione) factory TicketModel.empty({String? companyId, String? storeId}) { return TicketModel( companyId: companyId ?? '', storeId: storeId, ticketType: TicketType.repair, // Valore di default ticketStatus: TicketStatus.open, customerPrice: 0.0, internalCost: 0.0, hasCourtesyDevice: false, request: '', ); } TicketModel copyWith({ String? id, DateTime? createdAt, String? companyId, String? storeId, String? customerId, String? targetModelId, String? targetSn, String? targetPassword, String? sourceModelId, String? sourceSn, String? sourcePassword, double? customerPrice, double? internalCost, DateTime? closedAt, DateTime? returnedAt, String? request, WarrantyType? warrantyType, String? publicNotes, String? internalNotes, String? referenceId, String? alternativePhoneNumber, bool? hasCourtesyDevice, TicketType? ticketType, TicketStatus? ticketStatus, DateTime? estimatedDeliveryAt, TicketResult? ticketResult, String? resolutionNotes, CustomerModel? customer, String? targetModelName, String? sourceModelName, String? createdById, String? createdByName, String? assignedToId, String? assignedToName, String? includedAccessories, }) { return TicketModel( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, companyId: companyId ?? this.companyId, storeId: storeId ?? this.storeId, customerId: customerId ?? this.customerId, targetModelId: targetModelId ?? this.targetModelId, targetSn: targetSn ?? this.targetSn, targetPassword: targetPassword ?? this.targetPassword, sourceModelId: sourceModelId ?? this.sourceModelId, sourceSn: sourceSn ?? this.sourceSn, sourcePassword: sourcePassword ?? this.sourcePassword, customerPrice: customerPrice ?? this.customerPrice, internalCost: internalCost ?? this.internalCost, closedAt: closedAt ?? this.closedAt, returnedAt: returnedAt ?? this.returnedAt, request: request ?? this.request, warrantyType: warrantyType ?? this.warrantyType, publicNotes: publicNotes ?? this.publicNotes, internalNotes: internalNotes ?? this.internalNotes, referenceId: referenceId ?? this.referenceId, alternativePhoneNumber: alternativePhoneNumber ?? this.alternativePhoneNumber, hasCourtesyDevice: hasCourtesyDevice ?? this.hasCourtesyDevice, ticketType: ticketType ?? this.ticketType, ticketStatus: ticketStatus ?? this.ticketStatus, estimatedDeliveryAt: estimatedDeliveryAt ?? this.estimatedDeliveryAt, ticketResult: ticketResult ?? this.ticketResult, resolutionNotes: resolutionNotes ?? this.resolutionNotes, customer: customer ?? this.customer, targetModelName: targetModelName ?? this.targetModelName, sourceModelName: sourceModelName ?? this.sourceModelName, createdById: createdById ?? this.createdById, createdByName: createdByName ?? this.createdByName, assignedToId: assignedToId ?? this.assignedToId, assignedToName: assignedToName ?? this.assignedToName, includedAccessories: includedAccessories ?? this.includedAccessories, ); } /// Deserializzazione da Supabase factory TicketModel.fromMap(Map map) { return TicketModel( id: map['id'] as String, createdAt: map['created_at'] != null ? DateTime.parse(map['created_at']).toLocal() : null, companyId: map['company_id'] as String, storeId: map['store_id'] as String?, customerId: map['customer_id'] as String?, targetModelId: map['target_model_id'] as String?, targetSn: map['target_sn'] as String?, sourceModelId: map['source_model_id'] as String?, sourceSn: map['source_sn'] as String?, targetPassword: map['target_password'] as String?, sourcePassword: map['source_password'] as String?, // Fix per i field numerici di Postgres che potrebbero arrivare come int o double customerPrice: (map['customer_price'] as num?)?.toDouble() ?? 0.0, internalCost: (map['internal_cost'] as num?)?.toDouble() ?? 0.0, closedAt: map['closed_at'] != null ? DateTime.parse(map['closed_at']).toLocal() : null, returnedAt: map['returned_at'] != null ? DateTime.parse(map['returned_at']).toLocal() : null, request: map['request'] as String? ?? '', warrantyType: WarrantyType.fromString(map['warranty_type'] as String?), publicNotes: map['public_notes'] as String?, internalNotes: map['internal_notes'] as String?, referenceId: map['reference_id'] as String?, alternativePhoneNumber: map['alternative_phone_number'] as String?, hasCourtesyDevice: map['has_courtesy_device'] as bool? ?? false, ticketType: TicketType.fromString(map['ticket_type'] as String), ticketStatus: TicketStatus.fromString(map['ticket_status'] as String), estimatedDeliveryAt: map['estimated_delivery_at'] != null ? DateTime.parse(map['estimated_delivery_at']).toLocal() : null, ticketResult: TicketResult.fromString(map['ticket_result'] as String?), resolutionNotes: map['resolution_notes'] as String?, customer: map['customer'] != null ? CustomerModel.fromMap(map['customer'] as Map) : null, targetModelName: (map['target_model']?['name_with_brand'] as String?) ?.myFormat(), sourceModelName: (map['source_model']?['name_with_brand'] as String?) ?.myFormat(), createdById: map['created_by_id'] as String?, createdByName: (map['created_by']?['name'] as String?)?.myFormat(), assignedToId: map['assigned_to_id'] as String?, assignedToName: (map['assigned_to']?['name'] as String?)?.myFormat(), includedAccessories: map['included_accessories'] as String?, ); } /// Serializzazione per Supabase Map toMap() { return { if (id != null) 'id': id, 'company_id': companyId, 'store_id': storeId, 'customer_id': customerId, 'target_model_id': targetModelId, 'target_sn': targetSn, 'source_model_id': sourceModelId, 'source_sn': sourceSn, 'target_password': targetPassword, 'source_password': sourcePassword, 'customer_price': customerPrice, 'internal_cost': internalCost, if (closedAt != null) 'closed_at': closedAt!.toUtc().toIso8601String(), if (returnedAt != null) 'returned_at': returnedAt!.toUtc().toIso8601String(), 'request': request, 'created_by_id': createdById, 'warranty_type': warrantyType?.value ?? WarrantyType.noWarranty.value, 'public_notes': publicNotes, 'internal_notes': internalNotes, 'reference_id': referenceId, 'alternative_phone_number': alternativePhoneNumber, 'has_courtesy_device': hasCourtesyDevice, 'ticket_type': ticketType.value, 'ticket_status': ticketStatus.value, if (estimatedDeliveryAt != null) 'estimated_delivery_at': estimatedDeliveryAt!.toUtc().toIso8601String(), if (ticketResult != null) 'ticket_result': ticketResult!.value, 'resolution_notes': resolutionNotes, 'included_accessories': includedAccessories, }; } @override List get props => [ id, createdAt, companyId, storeId, customerId, targetModelId, targetSn, sourceModelId, sourceSn, targetPassword, sourcePassword, customerPrice, internalCost, closedAt, returnedAt, request, warrantyType, publicNotes, internalNotes, alternativePhoneNumber, hasCourtesyDevice, ticketType, ticketStatus, estimatedDeliveryAt, ticketResult, resolutionNotes, includedAccessories, customer, targetModelName, sourceModelName, createdById, createdByName, assignedToId, assignedToName, ]; }