2026-04-16 11:50:29 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
|
|
|
import '../models/service_model.dart';
|
|
|
|
|
|
|
|
|
|
class ServicesRepository {
|
|
|
|
|
final _supabase = Supabase.instance.client;
|
|
|
|
|
|
|
|
|
|
// --- RECUPERO PAGINATO CON FILTRI E JOIN ---
|
|
|
|
|
Future<List<ServiceModel>> fetchServices({
|
|
|
|
|
required String companyId,
|
|
|
|
|
required int offset,
|
|
|
|
|
int limit = 50,
|
|
|
|
|
String? searchTerm,
|
|
|
|
|
DateTimeRange? dateRange,
|
|
|
|
|
}) async {
|
|
|
|
|
try {
|
|
|
|
|
// Nota: 'customer(name, surname)' serve per il display name nella card
|
|
|
|
|
var query = _supabase
|
|
|
|
|
.from('service')
|
|
|
|
|
.select('''
|
|
|
|
|
*,
|
|
|
|
|
customer(name, surname),
|
|
|
|
|
energy_service(*),
|
|
|
|
|
fin_service(*),
|
|
|
|
|
entertainment_service(*)
|
|
|
|
|
''')
|
|
|
|
|
.eq('company_id', companyId);
|
|
|
|
|
|
|
|
|
|
// Filtro Range Date
|
|
|
|
|
if (dateRange != null) {
|
|
|
|
|
query = query
|
|
|
|
|
.gte('created_at', dateRange.start.toIso8601String())
|
|
|
|
|
.lte('created_at', dateRange.end.toIso8601String());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (searchTerm != null && searchTerm.isNotEmpty) {
|
|
|
|
|
// Filtra sui campi della tabella principale O su quelli della tabella joinata
|
|
|
|
|
query = query.or(
|
|
|
|
|
'number.ilike.%$searchTerm%,note.ilike.%$searchTerm%,customer.name.ilike.%$searchTerm%,customer.surname.ilike.%$searchTerm%',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final response = await query
|
|
|
|
|
.order('created_at', ascending: false)
|
|
|
|
|
.range(offset, offset + limit - 1);
|
|
|
|
|
|
|
|
|
|
return (response as List)
|
|
|
|
|
.map((map) => ServiceModel.fromMap(map))
|
|
|
|
|
.toList();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw Exception('Errore nel caricamento servizi: $e');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) ---
|
|
|
|
|
Future<void> saveFullService(ServiceModel service) async {
|
|
|
|
|
try {
|
2026-04-17 19:19:01 +02:00
|
|
|
// 1. Upsert del record principale
|
2026-04-16 11:50:29 +02:00
|
|
|
final serviceData = await _supabase
|
|
|
|
|
.from('service')
|
|
|
|
|
.upsert(service.toMap())
|
|
|
|
|
.select()
|
|
|
|
|
.single();
|
|
|
|
|
|
|
|
|
|
final String newId = serviceData['id'];
|
|
|
|
|
|
2026-04-17 19:19:01 +02:00
|
|
|
// 2. MODIFICA: Pulizia atomica dei figli
|
|
|
|
|
// Se stiamo modificando (id != null), resettiamo le tabelle collegate
|
2026-04-16 11:50:29 +02:00
|
|
|
if (service.id != null) {
|
2026-04-17 19:19:01 +02:00
|
|
|
await Future.wait([
|
|
|
|
|
_supabase.from('energy_service').delete().eq('service_id', newId),
|
|
|
|
|
_supabase.from('fin_service').delete().eq('service_id', newId),
|
|
|
|
|
_supabase
|
|
|
|
|
.from('entertainment_service')
|
|
|
|
|
.delete()
|
|
|
|
|
.eq('service_id', newId),
|
|
|
|
|
// Aggiungi qui eventuali altre tabelle pivot o file
|
|
|
|
|
]);
|
2026-04-16 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 19:19:01 +02:00
|
|
|
// 3. Inserimento dei moduli in parallelo per velocità
|
|
|
|
|
final List<Future> insertTasks = [];
|
|
|
|
|
|
2026-04-16 11:50:29 +02:00
|
|
|
if (service.energyServices.isNotEmpty) {
|
2026-04-17 19:19:01 +02:00
|
|
|
insertTasks.add(
|
|
|
|
|
_supabase
|
|
|
|
|
.from('energy_service')
|
|
|
|
|
.insert(
|
|
|
|
|
service.energyServices
|
|
|
|
|
.map((item) => item.copyWith(serviceId: newId).toMap())
|
|
|
|
|
.toList(),
|
|
|
|
|
),
|
|
|
|
|
);
|
2026-04-16 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (service.finServices.isNotEmpty) {
|
2026-04-17 19:19:01 +02:00
|
|
|
insertTasks.add(
|
|
|
|
|
_supabase
|
|
|
|
|
.from('fin_service')
|
|
|
|
|
.insert(
|
|
|
|
|
service.finServices
|
|
|
|
|
.map((item) => item.copyWith(serviceId: newId).toMap())
|
|
|
|
|
.toList(),
|
|
|
|
|
),
|
|
|
|
|
);
|
2026-04-16 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (service.entertainmentServices.isNotEmpty) {
|
2026-04-17 19:19:01 +02:00
|
|
|
insertTasks.add(
|
|
|
|
|
_supabase
|
|
|
|
|
.from('entertainment_service')
|
|
|
|
|
.insert(
|
|
|
|
|
service.entertainmentServices
|
|
|
|
|
.map((item) => item.copyWith(serviceId: newId).toMap())
|
|
|
|
|
.toList(),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (insertTasks.isNotEmpty) {
|
|
|
|
|
await Future.wait(insertTasks);
|
2026-04-16 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
} catch (e) {
|
2026-04-17 19:19:01 +02:00
|
|
|
// Qui potresti aggiungere una logica di "rollback manuale" se necessario
|
|
|
|
|
throw Exception('Errore durante il salvataggio corazzato: $e');
|
2026-04-16 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- ELIMINAZIONE ---
|
|
|
|
|
Future<void> deleteService(String id) async {
|
|
|
|
|
try {
|
|
|
|
|
await _supabase.from('service').delete().eq('id', id);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw Exception('Errore durante l\'eliminazione: $e');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|