Files
flux/lib/features/operations/blocs/operations_cubit.dart

362 lines
10 KiB
Dart
Raw Normal View History

import 'package:equatable/equatable.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
2026-04-29 19:25:48 +02:00
import 'package:flux/core/utils/extensions.dart';
2026-05-01 10:11:44 +02:00
import 'package:flux/features/operations/data/operations_repository.dart';
import 'package:flux/features/operations/models/energy_operation_model.dart';
import 'package:flux/features/operations/models/entertainment_operation_model.dart';
import 'package:flux/features/operations/models/fin_operation_model.dart';
import 'package:flux/features/operations/models/operation_file_model.dart';
import 'package:flux/features/operations/models/operation_model.dart';
import 'package:get_it/get_it.dart';
import 'package:collection/collection.dart';
2026-05-01 10:11:44 +02:00
part 'operations_state.dart';
2026-05-01 10:11:44 +02:00
class OperationsCubit extends Cubit<OperationsState> {
final OperationsRepository _repository = GetIt.I<OperationsRepository>();
final SessionCubit _sessionCubit = GetIt.I<SessionCubit>();
2026-05-01 10:11:44 +02:00
OperationsCubit()
: super(const OperationsState(status: OperationsStatus.initial));
// --- CARICAMENTO E PAGINAZIONE ---
2026-05-01 10:11:44 +02:00
Future<void> loadOperations({bool refresh = false}) async {
// Se stiamo già caricando, evitiamo chiamate doppie
2026-05-01 10:11:44 +02:00
if (state.status == OperationsStatus.loading) return;
// Se non è un refresh e abbiamo già raggiunto la fine dei dati, ci fermiamo
if (!refresh && state.hasReachedMax) return;
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: OperationsStatus.loading,
errorMessage: null,
// Se è un refresh, svuotiamo la lista attuale per mostrare lo shimmer/loading
2026-05-01 10:11:44 +02:00
allOperations: refresh ? [] : state.allOperations,
hasReachedMax: refresh ? false : state.hasReachedMax,
),
);
try {
2026-05-01 10:11:44 +02:00
final currentOffset = refresh ? 0 : state.allOperations.length;
final companyId = _sessionCubit.state.company?.id;
if (companyId == null) {
throw Exception("Company ID non trovato nella sessione");
}
2026-05-01 10:11:44 +02:00
final newOperations = await _repository.fetchOperations(
companyId: companyId,
offset: currentOffset,
limit: 50,
searchTerm: state.query,
dateRange: state.dateRange,
);
// Se ricevi meno record del limite, significa che non ce ne sono altri sul DB
2026-05-01 10:11:44 +02:00
final bool reachedMax = newOperations.length < 50;
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: OperationsStatus.ready,
allOperations: refresh
? newOperations
: [...state.allOperations, ...newOperations],
hasReachedMax: reachedMax,
),
);
} catch (e) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: OperationsStatus.failure,
errorMessage: "Errore nel caricamento servizi: $e",
),
);
}
}
// --- GESTIONE FILTRI ---
/// Aggiorna i parametri di ricerca e ricarica da zero
void updateFilters({String? query, DateTimeRange? range}) {
emit(
state.copyWith(
query: query ?? state.query,
dateRange: range ?? state.dateRange,
),
);
2026-05-01 10:11:44 +02:00
loadOperations(refresh: true);
}
/// Pulisce tutti i filtri
void clearFilters() {
emit(state.copyWith(query: '', dateRange: null));
2026-05-01 10:11:44 +02:00
loadOperations(refresh: true);
}
// --- GESTIONE BOZZA (DRAFT) ---
/// Inizializza un nuovo servizio o ne carica uno esistente per la modifica
2026-05-01 10:11:44 +02:00
void initOperationForm({
OperationModel? existingOperation,
String? operationId,
}) async {
2026-05-01 10:11:44 +02:00
if (existingOperation != null) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: existingOperation,
status: OperationsStatus.ready,
),
);
2026-05-01 10:11:44 +02:00
} else if (operationId != null) {
OperationModel? operationModel = state.allOperations.firstWhereOrNull(
(s) => s.id == operationId,
);
2026-05-01 10:11:44 +02:00
operationModel ??= await _repository.fetchOperationById(operationId);
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: operationModel,
status: OperationsStatus.ready,
),
);
} else {
// Crea un template vuoto con lo store di default (se disponibile)
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: OperationModel(
storeId: _sessionCubit.state.currentStore?.id ?? '',
number: '', // Sarà compilato dall'utente
createdAt: DateTime.now(),
companyId: _sessionCubit.state.company!.id!,
),
2026-05-01 10:11:44 +02:00
status: OperationsStatus.ready,
),
);
}
}
/// Metodo generico per aggiornare i campi base (AL, MNP, Note, ecc.)
void updateField({
int? al,
int? mnp,
int? nip,
int? unica,
int? telepass,
String? note,
String? number,
bool? isBozza,
bool? resultOk,
String? customerId,
String? customerDisplayName,
}) {
2026-05-01 10:11:44 +02:00
if (state.currentOperation == null) return;
2026-05-01 10:11:44 +02:00
final updated = state.currentOperation!.copyWith(
al: al,
mnp: mnp,
nip: nip,
unica: unica,
telepass: telepass,
note: note,
number: number,
isBozza: isBozza,
resultOk: resultOk,
customerId: customerId,
customerDisplayName: customerDisplayName,
);
2026-05-01 10:11:44 +02:00
emit(state.copyWith(currentOperation: updated));
}
// --- GESTIONE MODULI COMPLESSI ---
2026-05-01 10:11:44 +02:00
void updateEnergyOperations(List<EnergyOperationModel> energyList) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: state.currentOperation?.copyWith(
energyOperations: energyList,
),
),
);
}
2026-05-01 10:11:44 +02:00
void updateFinOperations(List<FinOperationModel> finList) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: state.currentOperation?.copyWith(
finOperations: finList,
),
),
);
}
2026-05-01 10:11:44 +02:00
void updateEntertainmentOperations(
List<EntertainmentOperationModel> entList,
) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: state.currentOperation?.copyWith(
entertainmentOperations: entList,
),
),
);
}
// --- PERSISTENZA ---
2026-05-01 10:11:44 +02:00
Future<void> saveCurrentOperation({
required bool isBozza,
bool shouldPop = true,
2026-05-01 10:11:44 +02:00
List<OperationFileModel>? files,
}) async {
2026-05-01 10:11:44 +02:00
if (state.currentOperation == null) return;
2026-05-01 10:11:44 +02:00
emit(state.copyWith(status: OperationsStatus.saving, errorMessage: null));
try {
// 1. Aggiorniamo il flag bozza in base a quale pulsante ha premuto l'utente
2026-05-01 10:11:44 +02:00
final operationToSave = state.currentOperation!.copyWith(
isBozza: isBozza,
files: files,
);
// 2. Salvataggio corazzato
2026-05-01 10:11:44 +02:00
final updatedOperation = await _repository.saveFullOperation(
operationToSave,
);
// 3. Reset e ricaricamento
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: shouldPop
? OperationsStatus.saved
: OperationsStatus.savedNoPop,
currentOperation: shouldPop ? null : updatedOperation,
),
);
2026-05-01 10:11:44 +02:00
await loadOperations(refresh: true);
} catch (e) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: OperationsStatus.failure,
errorMessage: e.toString(),
),
);
}
}
// --- GESTIONE ALLEGATI LOCALI ---
void addAttachments(List<PlatformFile> files) {
final newAttachments = files.map((file) {
2026-05-01 10:11:44 +02:00
return OperationFileModel(
id: null, // Meglio null se non è su DB
2026-05-01 10:11:44 +02:00
operationId: state.currentOperation?.id ?? '',
name: file.name.fileNameWithoutExtension(),
extension: file.name.fileExtension(),
storagePath: '',
fileSize: file.size,
localBytes: file.bytes,
createdAt: DateTime.now(),
);
}).toList();
// Creiamo una nuova lista pulita
2026-05-01 10:11:44 +02:00
final List<OperationFileModel> updatedList = [
...(state.currentOperation?.files ?? []),
...newAttachments,
];
2026-05-01 10:11:44 +02:00
// Emettiamo lo stato assicurandoci che il OperationModel venga clonato
if (state.currentOperation != null) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: state.currentOperation!.copyWith(
files: updatedList,
),
),
);
}
}
void removeAttachment(int index) {
2026-05-01 10:11:44 +02:00
if (state.currentOperation == null) return;
2026-05-01 10:11:44 +02:00
final updatedList = List<OperationFileModel>.from(
state.currentOperation!.files,
);
updatedList.removeAt(index);
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
currentOperation: state.currentOperation?.copyWith(files: updatedList),
),
);
}
2026-05-01 10:11:44 +02:00
void saveAndCopyFileToCustomer(List<OperationFileModel> selectedFiles) async {
final currentOperation = state.currentOperation;
// 1. Check di sicurezza: se non c'è il cliente, non sappiamo dove copiare
2026-05-01 10:11:44 +02:00
if (currentOperation == null || currentOperation.customerId == null) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: OperationsStatus.failure,
errorMessage:
"Impossibile copiare: nessun cliente associato alla pratica.",
),
);
return;
}
2026-05-01 10:11:44 +02:00
emit(state.copyWith(status: OperationsStatus.loading));
try {
// 2. SALVATAGGIO CORAZZATO
// Chiamiamo il repo e otteniamo la pratica con TUTTI i file ora dotati di ID e storagePath
2026-05-01 10:11:44 +02:00
final updatedOperation = await _repository.saveFullOperation(
currentOperation,
);
// 3. COPIA RELAZIONALE
// Per ogni file che l'utente ha selezionato nella UI, cerchiamo la sua versione
// "ufficiale" (quella con lo storagePath) nel modello appena tornato dal DB.
for (var selectedFile in selectedFiles) {
// Cerchiamo il match nel modello aggiornato
2026-05-01 10:11:44 +02:00
final persistedFile = updatedOperation.files.firstWhere(
(f) =>
f.name == selectedFile.name &&
f.extension == selectedFile.extension,
orElse: () => throw Exception(
"File ${selectedFile.name} non trovato dopo il salvataggio.",
),
);
// Creiamo il link nel database del cliente
await _repository.copyFileToCustomer(
file: persistedFile,
2026-05-01 10:11:44 +02:00
customerId: currentOperation.customerId!,
);
}
// 4. AGGIORNAMENTO STATO
// Aggiorniamo il Cubit con il servizio salvato così la UI mostra i file come "Remoti"
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: OperationsStatus.success,
currentOperation: updatedOperation,
),
);
} catch (e) {
emit(
state.copyWith(
2026-05-01 10:11:44 +02:00
status: OperationsStatus.failure,
errorMessage: "Errore durante il salvataggio e copia: $e",
),
);
}
}
}