@@ -2,6 +2,7 @@ import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
import 'package:flux/core/utils/extensions.dart';
|
||||
import 'package:flux/features/attachments/models/attachment_model.dart';
|
||||
import 'package:flux/features/customers/data/customer_repository.dart';
|
||||
import 'package:flux/features/customers/models/customer_file_model.dart';
|
||||
import 'package:flux/features/operations/models/operation_file_model.dart';
|
||||
@@ -21,11 +22,8 @@ class OperationsRepository {
|
||||
.from('operation')
|
||||
.select('''
|
||||
*,
|
||||
customer(nome),
|
||||
energy_operation(*),
|
||||
fin_operation(*),
|
||||
entertainment_operation(*),
|
||||
operation_file(*)
|
||||
customer(name),
|
||||
staff_member(name)
|
||||
''')
|
||||
.eq('id', id)
|
||||
.single();
|
||||
@@ -45,16 +43,13 @@ class OperationsRepository {
|
||||
DateTimeRange? dateRange,
|
||||
}) async {
|
||||
try {
|
||||
// Nota: 'customer(name, surname)' serve per il display name nella card
|
||||
var query = _supabase
|
||||
.from('operation')
|
||||
.select('''
|
||||
*,
|
||||
customer(nome),
|
||||
energy_operation(*),
|
||||
fin_operation(*),
|
||||
entertainment_operation(*),
|
||||
operation_file(*)
|
||||
customer(name),
|
||||
staff_member(name),
|
||||
attachments(*)
|
||||
''')
|
||||
.eq('company_id', companyId);
|
||||
|
||||
@@ -68,7 +63,7 @@ class OperationsRepository {
|
||||
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.nome.ilike.%$searchTerm%',
|
||||
'reference.ilike.%$searchTerm%,note.ilike.%$searchTerm%,customer.name.ilike.%$searchTerm%',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -80,7 +75,7 @@ class OperationsRepository {
|
||||
.map((map) => OperationModel.fromMap(map))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw Exception('Errore nel caricamento servizi: $e');
|
||||
throw Exception('$e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,66 +107,9 @@ class OperationsRepository {
|
||||
|
||||
final String newId = operationData['id'];
|
||||
|
||||
// 2. MODIFICA: Pulizia atomica dei figli
|
||||
// Se stiamo modificando (id != null), resettiamo le tabelle collegate
|
||||
if (operation.id != null) {
|
||||
await Future.wait([
|
||||
_supabase.from('energy_operation').delete().eq('operation_id', newId),
|
||||
_supabase.from('fin_operation').delete().eq('operation_id', newId),
|
||||
_supabase
|
||||
.from('entertainment_operation')
|
||||
.delete()
|
||||
.eq('operation_id', newId),
|
||||
// Aggiungi qui eventuali altre tabelle pivot o file
|
||||
]);
|
||||
}
|
||||
|
||||
// 3. Inserimento dei moduli in parallelo per velocità
|
||||
final List<Future> insertTasks = [];
|
||||
|
||||
if (operation.energyOperations.isNotEmpty) {
|
||||
insertTasks.add(
|
||||
_supabase
|
||||
.from('energy_operation')
|
||||
.insert(
|
||||
operation.energyOperations
|
||||
.map((item) => item.copyWith(operationId: newId).toMap())
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (operation.finOperations.isNotEmpty) {
|
||||
insertTasks.add(
|
||||
_supabase
|
||||
.from('fin_operation')
|
||||
.insert(
|
||||
operation.finOperations
|
||||
.map((item) => item.copyWith(operationId: newId).toMap())
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (operation.entertainmentOperations.isNotEmpty) {
|
||||
insertTasks.add(
|
||||
_supabase
|
||||
.from('entertainment_operation')
|
||||
.insert(
|
||||
operation.entertainmentOperations
|
||||
.map((item) => item.copyWith(operationId: newId).toMap())
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (insertTasks.isNotEmpty) {
|
||||
await Future.wait(insertTasks);
|
||||
}
|
||||
|
||||
// 4. UPLOAD DEI FILE LOCALI (Nuovi)
|
||||
// Filtriamo solo i file che non hanno ancora un ID (quindi sono locali)
|
||||
final localFilesToUpload = operation.files
|
||||
final localFilesToUpload = operation.attachments
|
||||
.where((f) => f.id == null)
|
||||
.toList();
|
||||
|
||||
@@ -202,7 +140,7 @@ class OperationsRepository {
|
||||
);
|
||||
|
||||
// B. Inserimento riga nel DB relazionale
|
||||
await _supabase.from('operation_file').insert(fileToSave.toMap());
|
||||
await _supabase.from('attachment').insert(fileToSave.toMap());
|
||||
}
|
||||
|
||||
uploadTasks.add(uploadAndLink());
|
||||
@@ -219,10 +157,9 @@ class OperationsRepository {
|
||||
.from('operation')
|
||||
.select('''
|
||||
*,
|
||||
energy_operation(*),
|
||||
fin_operation(*),
|
||||
entertainment_operation(*),
|
||||
operation_file(*)
|
||||
staff_member(name),
|
||||
customer(name),
|
||||
attachments(*)
|
||||
''')
|
||||
.eq('id', newId)
|
||||
.single();
|
||||
@@ -230,7 +167,7 @@ class OperationsRepository {
|
||||
return OperationModel.fromMap(updatedOperationData);
|
||||
} catch (e) {
|
||||
// Qui potresti aggiungere una logica di "rollback manuale" se necessario
|
||||
throw Exception('Errore durante il salvataggio corazzato: $e');
|
||||
throw Exception('$e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +176,7 @@ class OperationsRepository {
|
||||
try {
|
||||
await _supabase.from('operation').delete().eq('id', id);
|
||||
} catch (e) {
|
||||
throw Exception('Errore durante l\'eliminazione: $e');
|
||||
throw Exception('$e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,16 +186,17 @@ class OperationsRepository {
|
||||
// Cerchiamo i tipi più frequenti associati ai servizi di questa company
|
||||
// Nota: dobbiamo passare attraverso la tabella 'operation' per filtrare per company_id
|
||||
final response = await _supabase
|
||||
.from('entertainment_operation')
|
||||
.select('type, operation!inner(store!inner(company_id))')
|
||||
.eq('operation.store.company_id', companyId)
|
||||
.limit(100); // Prendiamo un campione
|
||||
.from('operation')
|
||||
.select('description')
|
||||
.eq('company_id', companyId)
|
||||
.eq('type', 'Entertainment')
|
||||
.limit(50); // Prendiamo un campione
|
||||
|
||||
// Logica rapida per contare le occorrenze e prendere i primi 5
|
||||
final Map<String, int> counts = {};
|
||||
for (var item in (response as List)) {
|
||||
final type = item['type'] as String;
|
||||
counts[type] = (counts[type] ?? 0) + 1;
|
||||
final description = item['description'] as String;
|
||||
counts[description] = (counts[description] ?? 0) + 1;
|
||||
}
|
||||
|
||||
var sortedKeys = counts.keys.toList()
|
||||
@@ -276,19 +214,19 @@ class OperationsRepository {
|
||||
}
|
||||
|
||||
/// Ascolta in tempo reale i file caricati per una pratica
|
||||
Stream<List<OperationFileModel>> getOperationFilesStream(String operationId) {
|
||||
Stream<List<AttachmentModel>> getOperationFilesStream(String operationId) {
|
||||
return _supabase
|
||||
.from('operation_file')
|
||||
.from('attachment')
|
||||
.stream(primaryKey: ['id'])
|
||||
.eq('operation_id', operationId)
|
||||
.order('created_at', ascending: false)
|
||||
.map(
|
||||
(listOfMaps) =>
|
||||
listOfMaps.map((map) => OperationFileModel.fromMap(map)).toList(),
|
||||
listOfMaps.map((map) => AttachmentModel.fromMap(map)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<OperationFileModel> uploadAndRegisterOperationFile({
|
||||
Future<AttachmentModel> uploadAndRegisterOperationFile({
|
||||
required String operationId,
|
||||
required PlatformFile pickedFile,
|
||||
}) async {
|
||||
@@ -299,7 +237,8 @@ class OperationsRepository {
|
||||
final storagePath =
|
||||
'$companyId/operations/$operationId/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName';
|
||||
final int fileSize = pickedFile.size;
|
||||
final fileToSave = OperationFileModel(
|
||||
final fileToSave = AttachmentModel(
|
||||
companyId: GetIt.I.get<SessionCubit>().state.company!.id!,
|
||||
operationId: operationId,
|
||||
name: cleanFileName.fileNameWithoutExtension(),
|
||||
extension: cleanFileName.fileExtension(),
|
||||
@@ -327,12 +266,12 @@ class OperationsRepository {
|
||||
}
|
||||
|
||||
final response = await _supabase
|
||||
.from('operation_file')
|
||||
.from('attachment')
|
||||
.insert(fileToSave.toMap())
|
||||
.select()
|
||||
.single();
|
||||
|
||||
return OperationFileModel.fromMap(response);
|
||||
return AttachmentModel.fromMap(response);
|
||||
} catch (e) {
|
||||
throw 'Errore durante l\'upload: $e';
|
||||
}
|
||||
@@ -342,17 +281,13 @@ class OperationsRepository {
|
||||
required OperationFileModel file,
|
||||
required String customerId,
|
||||
}) async {
|
||||
CustomerFileModel fileToCopy = CustomerFileModel(
|
||||
customerId: customerId,
|
||||
name: file.name,
|
||||
storagePath: file.storagePath,
|
||||
extension: file.extension,
|
||||
fileSize: file.fileSize,
|
||||
);
|
||||
await _customerRepository.saveFileReference(fileToCopy);
|
||||
await _supabase
|
||||
.from('attachment')
|
||||
.update({'customer_id': customerId})
|
||||
.eq('id', file.id!);
|
||||
}
|
||||
|
||||
Future<void> deleteOperationFiles(List<OperationFileModel> files) async {
|
||||
Future<void> deleteOperationFiles(List<AttachmentModel> files) async {
|
||||
if (files.isEmpty) return;
|
||||
// 1. Prepariamo le liste di ID e di Percorsi
|
||||
final List<String> idsToDelete = files.map((f) => f.id!).toList();
|
||||
@@ -360,18 +295,14 @@ class OperationsRepository {
|
||||
|
||||
try {
|
||||
await _supabase
|
||||
.from('operation_file')
|
||||
.delete()
|
||||
.from('attachment')
|
||||
.update({'operation_id': null})
|
||||
.inFilter('id', idsToDelete);
|
||||
|
||||
await _supabase.storage.from('documents').remove(storagePaths);
|
||||
|
||||
debugPrint("Eliminati con successo ${files.length} file.");
|
||||
} on PostgrestException catch (e) {
|
||||
debugPrint("Errore DB: ${e.message}");
|
||||
throw 'Errore database: ${e.message}';
|
||||
} catch (e) {
|
||||
debugPrint("Errore generico: $e");
|
||||
throw 'Errore durante l\'eliminazione dei file: $e';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user