Files
flux/lib/features/tickets/data/ticket_repository.dart

289 lines
9.5 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
2026-05-20 11:03:33 +02:00
import 'package:flux/core/enums_and_consts/consts.dart';
2026-05-16 14:30:23 +02:00
import 'package:flux/features/settings/document_sequence/data/document_sequence_repository.dart';
import 'package:flux/features/settings/document_sequence/models/document_sequence_model.dart';
import 'package:flux/features/tickets/models/ticket_model.dart';
import 'package:get_it/get_it.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class TicketRepository {
final SupabaseClient _supabase = GetIt.I.get<SupabaseClient>();
2026-05-16 14:30:23 +02:00
final DocumentSequenceRepository _documentSequenceRepository = GetIt.I
.get<DocumentSequenceRepository>();
TicketRepository();
2026-05-20 11:03:33 +02:00
static const String _tableName = Tables.tickets;
// --- RECUPERO PAGINATO CON FILTRI E JOIN DEI TICKET DI UNO STORE ---
Future<List<TicketModel>> fetchStoreTickets({
required int offset,
int limit = 50,
String? searchTerm,
DateTimeRange? dateRange,
TicketStatus? statusFilter,
TicketType? ticketTypeFilter,
String? staffIdFilter,
}) async {
try {
var query = _supabase
.from(_tableName)
.select('''
2026-05-20 11:03:33 +02:00
*,
2026-05-21 19:29:46 +02:00
customer:${Tables.customers}!ticket_customer_id_fkey (*),
2026-05-20 11:03:33 +02:00
${Tables.shippingDocuments} (*, ${Tables.attachments} (*)),
created_by:${Tables.staffMembers}!ticket_staff_id_fkey (*),
assigned_to:${Tables.staffMembers}!ticket_assigned_to_id_fkey (*),
target_model:${Tables.models}!ticket_model_id_1_fkey (*),
source_model:${Tables.models}!ticket_model_id_2_fkey (*)
''')
.eq('store_id', GetIt.I.get<SessionCubit>().state.currentStore!.id!);
// Filtro Range Date
if (dateRange != null) {
query = query
.gte('created_at', dateRange.start.toIso8601String())
.lte('created_at', dateRange.end.toIso8601String());
}
if (statusFilter != null) {
query = query.eq('status', statusFilter.value);
}
if (ticketTypeFilter != null) {
query = query.eq('ticket_type', ticketTypeFilter.value);
}
if (staffIdFilter != null) {
query = query.eq('staff_id', staffIdFilter);
}
if (searchTerm != null && searchTerm.isNotEmpty) {
// Filtra sui campi della tabella principale O su quelli della tabella joinata
query = query.or('customer.name.ilike.%$searchTerm%');
}
final response = await query
.order('created_at', ascending: false)
.range(offset, offset + limit - 1);
return (response as List).map((map) => TicketModel.fromMap(map)).toList();
} catch (e) {
throw Exception('$e');
}
}
// --- RECUPERO PAGINATO CON FILTRI E JOIN DEI TICKET DI TUTTA L'AZIENDA ---
2026-05-30 16:26:59 +02:00
Future<List<TicketModel>> fetchTickets({
required String? companyId,
String? storeId,
required int offset,
int limit = 50,
String? searchTerm,
DateTimeRange? dateRange,
TicketStatus? ticketStatusFilter,
TicketType? ticketTypeFilter,
String? staffIdFilter,
}) async {
try {
var query = _supabase
.from(_tableName)
.select('''
*,
2026-05-21 19:29:46 +02:00
customer:${Tables.customers}!ticket_customer_id_fkey (*),
2026-05-20 11:03:33 +02:00
${Tables.shippingDocuments} (*, ${Tables.attachments} (*)),
created_by:${Tables.staffMembers}!ticket_staff_id_fkey (*),
assigned_to:${Tables.staffMembers}!ticket_assigned_to_id_fkey (*),
target_model:${Tables.models}!ticket_model_id_1_fkey (*),
source_model:${Tables.models}!ticket_model_id_2_fkey (*)
''')
2026-05-30 16:26:59 +02:00
.eq('company_id', companyId!);
// Filtro Range Date
if (dateRange != null) {
query = query
.gte('created_at', dateRange.start.toIso8601String())
.lte('created_at', dateRange.end.toIso8601String());
}
2026-05-30 16:26:59 +02:00
if (storeId != null) {
query = query.or('store_id.eq.$storeId,store_id.is.null');
}
if (ticketStatusFilter != null) {
query = query.eq('status', ticketStatusFilter.value);
}
if (ticketTypeFilter != null) {
query = query.eq('ticket_type', ticketTypeFilter.value);
}
if (staffIdFilter != null) {
query = query.eq('staff_id', staffIdFilter);
}
if (searchTerm != null && searchTerm.isNotEmpty) {
// Filtra sui campi della tabella principale O su quelli della tabella joinata
query = query.or('customer.name.ilike.%$searchTerm%');
}
final response = await query
.order('created_at', ascending: false)
.range(offset, offset + limit - 1);
return (response as List).map((map) => TicketModel.fromMap(map)).toList();
} catch (e) {
throw Exception('$e');
}
}
/// Stream dei ticket che necessitano attenzione (es. in scadenza oggi o in ritardo)
Stream<List<TicketModel>> getAttentionNeededTicketsStream() {
return _supabase
.from(_tableName)
.stream(primaryKey: ['id'])
.eq('store_id', GetIt.I.get<SessionCubit>().state.currentStore!.id!)
// Purtroppo lo stream accetta solo filtri base, quindi ci facciamo
// mandare i dati e li filtriamo con la potenza di Dart!
.limit(300)
.map((listOfMaps) {
final now = DateTime.now();
final endOfToday = DateTime(now.year, now.month, now.day, 23, 59, 59);
// 1. Mappiamo tutto in TicketModel
final allStoreTickets = listOfMaps
.map((map) => TicketModel.fromMap(map))
.toList();
// 2. Filtriamo in memoria!
final urgentTickets = allStoreTickets.where((ticket) {
// Escludiamo quelli già chiusi o consegnati
if (ticket.ticketStatus == TicketStatus.closed ||
ticket.ticketStatus == TicketStatus.ready) {
return false;
}
// Se c'è una data di consegna stimata ed è <= a stasera, è urgente!
if (ticket.estimatedDeliveryAt != null) {
return ticket.estimatedDeliveryAt!.isBefore(endOfToday);
}
return false;
}).toList();
// 3. Li ordiniamo mettendo i più vecchi/urgenti in cima
urgentTickets.sort(
(a, b) => a.estimatedDeliveryAt!.compareTo(b.estimatedDeliveryAt!),
);
return urgentTickets;
});
}
2026-05-19 12:46:13 +02:00
Stream<List<TicketModel>> getLatestStoreTicketsStream({
required String storeId,
required int limit,
}) {
return _supabase
.from(_tableName)
.stream(primaryKey: ['id'])
.eq('store_id', storeId)
.order('created_at', ascending: false)
.limit(limit)
.map(
(listOfMaps) =>
listOfMaps.map((map) => TicketModel.fromMap(map)).toList(),
);
}
/// Recupera un ticket specifico CON TUTTE LE RELAZIONI espanse (Cliente e Modelli)
/// Questa è la vera magia di Supabase!
Future<TicketModel> getTicketById(String ticketId) async {
try {
// Usiamo i nomi esatti delle Foreign Key che hai definito nell'SQL!
final response = await _supabase
.from(_tableName)
.select('''
*,
2026-05-21 19:29:46 +02:00
customer:${Tables.customers}!ticket_customer_id_fkey (*),
2026-05-20 11:03:33 +02:00
target_model:${Tables.models}!ticket_model_id_1_fkey (*),
source_model:${Tables.models}!ticket_model_id_2_fkey (*),
created_by:${Tables.staffMembers}!ticket_staff_id_fkey (*),
assigned_to:${Tables.staffMembers}!ticket_assigned_to_id_fkey (*),
${Tables.shippingDocuments} (*, ${Tables.attachments} (*))
''')
.eq('id', ticketId)
.single();
return TicketModel.fromMap(response);
} catch (e) {
throw Exception('Errore nel recupero del dettaglio ticket: $e');
}
}
2026-05-10 14:09:57 +02:00
/// Salva il ticket
Future<TicketModel> insertTicket(TicketModel ticket) async {
if (ticket.id != null) {
throw Exception('Impossibile creare un ticket esistente, id not null');
}
try {
2026-05-10 14:09:57 +02:00
final ticketToSave = ticket.copyWith(
2026-05-16 14:30:23 +02:00
referenceId: await _documentSequenceRepository.getNextDocumentNumber(
DocumentType.ticket.name,
),
2026-05-10 14:09:57 +02:00
);
final response = await _supabase
.from(_tableName)
2026-05-10 14:09:57 +02:00
.insert(ticketToSave.toMap())
.select()
.single();
return TicketModel.fromMap(response);
} catch (e) {
throw Exception('Errore nella creazione del ticket: $e');
}
}
/// Aggiorna un ticket esistente
Future<TicketModel> updateTicket(TicketModel ticket) async {
if (ticket.id == null) {
throw Exception('Impossibile aggiornare un ticket senza ID');
}
try {
final response = await _supabase
.from(_tableName)
.update(ticket.toMap())
.eq('id', ticket.id!)
.select()
.single();
return TicketModel.fromMap(response);
} catch (e) {
throw Exception('Errore nell\'aggiornamento del ticket: $e');
}
}
2026-05-24 12:42:11 +02:00
/// Chiude i ticket in bulk
Future<void> closeTickets(List<String> ticketIds) async {
try {
await _supabase
.from(_tableName)
.update({'ticket_status': TicketStatus.closed.value})
.inFilter('id', ticketIds);
} catch (e) {
throw Exception('Errore nella chiusura dei ticket: $e');
}
}
/// Elimina (o annulla) un ticket
Future<void> deleteTicket(String ticketId) async {
try {
await _supabase.from(_tableName).delete().eq('id', ticketId);
} catch (e) {
throw Exception('Errore nell\'eliminazione del ticket: $e');
}
}
}