renamed services folder to operations
This commit is contained in:
348
lib/features/operations/blocs/services_cubit.dart
Normal file
348
lib/features/operations/blocs/services_cubit.dart
Normal file
@@ -0,0 +1,348 @@
|
||||
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';
|
||||
import 'package:flux/core/utils/extensions.dart';
|
||||
import 'package:flux/features/operations/data/services_repository.dart';
|
||||
import 'package:flux/features/operations/models/energy_service_model.dart';
|
||||
import 'package:flux/features/operations/models/entertainment_service_model.dart';
|
||||
import 'package:flux/features/operations/models/fin_service_model.dart';
|
||||
import 'package:flux/features/operations/models/service_file_model.dart';
|
||||
import 'package:flux/features/operations/models/service_model.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
part 'services_state.dart';
|
||||
|
||||
class ServicesCubit extends Cubit<ServicesState> {
|
||||
final ServicesRepository _repository = GetIt.I<ServicesRepository>();
|
||||
final SessionCubit _sessionCubit = GetIt.I<SessionCubit>();
|
||||
|
||||
ServicesCubit() : super(const ServicesState(status: ServicesStatus.initial));
|
||||
|
||||
// --- CARICAMENTO E PAGINAZIONE ---
|
||||
|
||||
Future<void> loadServices({bool refresh = false}) async {
|
||||
// Se stiamo già caricando, evitiamo chiamate doppie
|
||||
if (state.status == ServicesStatus.loading) return;
|
||||
|
||||
// Se non è un refresh e abbiamo già raggiunto la fine dei dati, ci fermiamo
|
||||
if (!refresh && state.hasReachedMax) return;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ServicesStatus.loading,
|
||||
errorMessage: null,
|
||||
// Se è un refresh, svuotiamo la lista attuale per mostrare lo shimmer/loading
|
||||
allServices: refresh ? [] : state.allServices,
|
||||
hasReachedMax: refresh ? false : state.hasReachedMax,
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
final currentOffset = refresh ? 0 : state.allServices.length;
|
||||
final companyId = _sessionCubit.state.company?.id;
|
||||
|
||||
if (companyId == null) {
|
||||
throw Exception("Company ID non trovato nella sessione");
|
||||
}
|
||||
|
||||
final newServices = await _repository.fetchServices(
|
||||
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
|
||||
final bool reachedMax = newServices.length < 50;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ServicesStatus.ready,
|
||||
allServices: refresh
|
||||
? newServices
|
||||
: [...state.allServices, ...newServices],
|
||||
hasReachedMax: reachedMax,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ServicesStatus.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,
|
||||
),
|
||||
);
|
||||
loadServices(refresh: true);
|
||||
}
|
||||
|
||||
/// Pulisce tutti i filtri
|
||||
void clearFilters() {
|
||||
emit(state.copyWith(query: '', dateRange: null));
|
||||
loadServices(refresh: true);
|
||||
}
|
||||
|
||||
// --- GESTIONE BOZZA (DRAFT) ---
|
||||
|
||||
/// Inizializza un nuovo servizio o ne carica uno esistente per la modifica
|
||||
void initServiceForm({
|
||||
ServiceModel? existingService,
|
||||
String? serviceId,
|
||||
}) async {
|
||||
if (existingService != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: existingService,
|
||||
status: ServicesStatus.ready,
|
||||
),
|
||||
);
|
||||
} else if (serviceId != null) {
|
||||
ServiceModel? serviceModel = state.allServices.firstWhereOrNull(
|
||||
(s) => s.id == serviceId,
|
||||
);
|
||||
serviceModel ??= await _repository.fetchServiceById(serviceId);
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: serviceModel,
|
||||
status: ServicesStatus.ready,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Crea un template vuoto con lo store di default (se disponibile)
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: ServiceModel(
|
||||
storeId: _sessionCubit.state.currentStore?.id ?? '',
|
||||
number: '', // Sarà compilato dall'utente
|
||||
createdAt: DateTime.now(),
|
||||
companyId: _sessionCubit.state.company!.id!,
|
||||
),
|
||||
status: ServicesStatus.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,
|
||||
}) {
|
||||
if (state.currentService == null) return;
|
||||
|
||||
final updated = state.currentService!.copyWith(
|
||||
al: al,
|
||||
mnp: mnp,
|
||||
nip: nip,
|
||||
unica: unica,
|
||||
telepass: telepass,
|
||||
note: note,
|
||||
number: number,
|
||||
isBozza: isBozza,
|
||||
resultOk: resultOk,
|
||||
customerId: customerId,
|
||||
customerDisplayName: customerDisplayName,
|
||||
);
|
||||
|
||||
emit(state.copyWith(currentService: updated));
|
||||
}
|
||||
|
||||
// --- GESTIONE MODULI COMPLESSI ---
|
||||
|
||||
void updateEnergyServices(List<EnergyServiceModel> energyList) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: state.currentService?.copyWith(
|
||||
energyServices: energyList,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void updateFinServices(List<FinServiceModel> finList) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: state.currentService?.copyWith(finServices: finList),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void updateEntertainmentServices(List<EntertainmentServiceModel> entList) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: state.currentService?.copyWith(
|
||||
entertainmentServices: entList,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --- PERSISTENZA ---
|
||||
|
||||
Future<void> saveCurrentService({
|
||||
required bool isBozza,
|
||||
bool shouldPop = true,
|
||||
List<ServiceFileModel>? files,
|
||||
}) async {
|
||||
if (state.currentService == null) return;
|
||||
|
||||
emit(state.copyWith(status: ServicesStatus.saving, errorMessage: null));
|
||||
try {
|
||||
// 1. Aggiorniamo il flag bozza in base a quale pulsante ha premuto l'utente
|
||||
final serviceToSave = state.currentService!.copyWith(
|
||||
isBozza: isBozza,
|
||||
files: files,
|
||||
);
|
||||
|
||||
// 2. Salvataggio corazzato
|
||||
final updatedService = await _repository.saveFullService(serviceToSave);
|
||||
|
||||
// 3. Reset e ricaricamento
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: shouldPop ? ServicesStatus.saved : ServicesStatus.savedNoPop,
|
||||
currentService: shouldPop ? null : updatedService,
|
||||
),
|
||||
);
|
||||
await loadServices(refresh: true);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ServicesStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- GESTIONE ALLEGATI LOCALI ---
|
||||
|
||||
void addAttachments(List<PlatformFile> files) {
|
||||
final newAttachments = files.map((file) {
|
||||
return ServiceFileModel(
|
||||
id: null, // Meglio null se non è su DB
|
||||
serviceId: state.currentService?.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
|
||||
final List<ServiceFileModel> updatedList = [
|
||||
...(state.currentService?.files ?? []),
|
||||
...newAttachments,
|
||||
];
|
||||
|
||||
// Emettiamo lo stato assicurandoci che il ServiceModel venga clonato
|
||||
if (state.currentService != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: state.currentService!.copyWith(files: updatedList),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void removeAttachment(int index) {
|
||||
if (state.currentService == null) return;
|
||||
|
||||
final updatedList = List<ServiceFileModel>.from(
|
||||
state.currentService!.files,
|
||||
);
|
||||
updatedList.removeAt(index);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentService: state.currentService?.copyWith(files: updatedList),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void saveAndCopyFileToCustomer(List<ServiceFileModel> selectedFiles) async {
|
||||
final currentService = state.currentService;
|
||||
|
||||
// 1. Check di sicurezza: se non c'è il cliente, non sappiamo dove copiare
|
||||
if (currentService == null || currentService.customerId == null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ServicesStatus.failure,
|
||||
errorMessage:
|
||||
"Impossibile copiare: nessun cliente associato alla pratica.",
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
emit(state.copyWith(status: ServicesStatus.loading));
|
||||
|
||||
try {
|
||||
// 2. SALVATAGGIO CORAZZATO
|
||||
// Chiamiamo il repo e otteniamo la pratica con TUTTI i file ora dotati di ID e storagePath
|
||||
final updatedService = await _repository.saveFullService(currentService);
|
||||
|
||||
// 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
|
||||
final persistedFile = updatedService.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,
|
||||
customerId: currentService.customerId!,
|
||||
);
|
||||
}
|
||||
|
||||
// 4. AGGIORNAMENTO STATO
|
||||
// Aggiorniamo il Cubit con il servizio salvato così la UI mostra i file come "Remoti"
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ServicesStatus.success,
|
||||
currentService: updatedService,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ServicesStatus.failure,
|
||||
errorMessage: "Errore durante il salvataggio e copia: $e",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user