reworked operation (#12)
Reviewed-on: #12 Co-authored-by: Mark M2 Macbook <marco@catelli.it> Co-committed-by: Mark M2 Macbook <marco@catelli.it>
This commit is contained in:
304
lib/features/operations/blocs/operations_cubit.dart
Normal file
304
lib/features/operations/blocs/operations_cubit.dart
Normal file
@@ -0,0 +1,304 @@
|
||||
import 'package:equatable/equatable.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/features/operations/data/operations_repository.dart';
|
||||
import 'package:flux/features/operations/models/operation_model.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
part 'operations_state.dart';
|
||||
|
||||
class OperationsCubit extends Cubit<OperationsState> {
|
||||
final OperationsRepository _repository = GetIt.I<OperationsRepository>();
|
||||
final SessionCubit _sessionCubit = GetIt.I<SessionCubit>();
|
||||
final Uuid _uuid = const Uuid(); // Generatore di UUID per il batch
|
||||
|
||||
OperationsCubit()
|
||||
: super(const OperationsState(status: OperationsStatus.initial));
|
||||
|
||||
// --- CARICAMENTO E PAGINAZIONE ---
|
||||
|
||||
Future<void> loadOperations({bool refresh = false}) async {
|
||||
if (state.status == OperationsStatus.loading) return;
|
||||
if (!refresh && state.hasReachedMax) return;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: OperationsStatus.loading,
|
||||
errorMessage: null,
|
||||
allOperations: refresh ? [] : state.allOperations,
|
||||
hasReachedMax: refresh ? false : state.hasReachedMax,
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
final currentOffset = refresh ? 0 : state.allOperations.length;
|
||||
final companyId = _sessionCubit.state.company?.id;
|
||||
|
||||
if (companyId == null) {
|
||||
throw Exception("Company ID non trovato nella sessione");
|
||||
}
|
||||
|
||||
final newOperations = await _repository.fetchOperations(
|
||||
companyId: companyId,
|
||||
offset: currentOffset,
|
||||
limit: 50,
|
||||
searchTerm: state.query,
|
||||
dateRange: state.dateRange,
|
||||
);
|
||||
|
||||
final bool reachedMax = newOperations.length < 50;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: OperationsStatus.ready,
|
||||
allOperations: refresh
|
||||
? newOperations
|
||||
: [...state.allOperations, ...newOperations],
|
||||
hasReachedMax: reachedMax,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: OperationsStatus.failure,
|
||||
errorMessage: "Errore nel caricamento operazioni: $e",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- GESTIONE FILTRI ---
|
||||
|
||||
void updateFilters({String? query, DateTimeRange? range}) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
query: query ?? state.query,
|
||||
dateRange: range ?? state.dateRange,
|
||||
),
|
||||
);
|
||||
loadOperations(refresh: true);
|
||||
}
|
||||
|
||||
void clearFilters() {
|
||||
emit(state.copyWith(query: '', dateRange: null));
|
||||
loadOperations(refresh: true);
|
||||
}
|
||||
|
||||
void initOperationForm({
|
||||
OperationModel? existingOperation,
|
||||
String? operationId,
|
||||
String? staffId,
|
||||
String? staffDisplayName,
|
||||
}) async {
|
||||
if (existingOperation != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentOperation: existingOperation,
|
||||
status: OperationsStatus.ready,
|
||||
),
|
||||
);
|
||||
} else if (operationId != null) {
|
||||
OperationModel? operationModel = state.allOperations.firstWhereOrNull(
|
||||
(s) => s.id == operationId,
|
||||
);
|
||||
operationModel ??= await _repository.fetchOperationById(operationId);
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentOperation: operationModel,
|
||||
status: OperationsStatus.ready,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// NUOVA PRATICA: Creiamo un nuovo Batch UUID
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentOperation: OperationModel(
|
||||
storeId: _sessionCubit.state.currentStore?.id ?? '',
|
||||
reference: '',
|
||||
createdAt: DateTime.now(),
|
||||
companyId: _sessionCubit.state.company!.id!,
|
||||
status: OperationStatus.draft,
|
||||
batchUuid: _uuid.v4(), // <-- GENERIAMO IL BATCH UNIVOCO
|
||||
),
|
||||
status: OperationsStatus.ready,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// MAGIA PURA: Prepara il form per inserire un altro servizio nella stessa pratica.
|
||||
/// Mantiene il Cliente, il Batch e lo Store, ma svuota il resto.
|
||||
void prepareNextOperationInBatch() {
|
||||
if (state.currentOperation == null) return;
|
||||
|
||||
final current = state.currentOperation!;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: OperationsStatus.ready,
|
||||
currentOperation: OperationModel(
|
||||
companyId: current.companyId,
|
||||
storeId: current.storeId,
|
||||
storeDisplayName: current.storeDisplayName,
|
||||
batchUuid: current.batchUuid, // <-- MANTIENE IL COLLEGAMENTO
|
||||
customerId: current.customerId, // <-- MANTIENE IL CLIENTE
|
||||
customerDisplayName: current.customerDisplayName,
|
||||
status: OperationStatus.draft,
|
||||
createdAt: DateTime.now(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --- PERSISTENZA ---
|
||||
|
||||
Future<void> saveCurrentOperation({
|
||||
required OperationStatus targetStatus,
|
||||
bool shouldPop = true,
|
||||
}) async {
|
||||
if (state.currentOperation == null) return;
|
||||
|
||||
emit(state.copyWith(status: OperationsStatus.saving, errorMessage: null));
|
||||
try {
|
||||
final operationToSave = state.currentOperation!.copyWith(
|
||||
status: targetStatus,
|
||||
);
|
||||
|
||||
final updatedOperation = await _repository.saveFullOperation(
|
||||
operation: operationToSave,
|
||||
);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
// Se non facciamo Pop (es. l'utente vuole aggiungere un altro servizio), non killiamo l'operazione corrente
|
||||
status: shouldPop
|
||||
? OperationsStatus.saved
|
||||
: OperationsStatus.savedNoPop,
|
||||
currentOperation: shouldPop ? null : updatedOperation,
|
||||
),
|
||||
);
|
||||
|
||||
// Ricarica in background per la dashboard
|
||||
loadOperations(refresh: true);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: OperationsStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- RECUPERO OPERAZIONI DELLO STESSO BATCH (Per UI di riepilogo) ---
|
||||
|
||||
/// Puoi usare questa funzione se nella UI vuoi mostrare "Hai inserito 3 servizi in questa pratica"
|
||||
List<OperationModel> getOperationsInCurrentBatch() {
|
||||
if (state.currentOperation == null) return [];
|
||||
final currentBatch = state.currentOperation!.batchUuid;
|
||||
|
||||
// Filtriamo dalla lista caricata (o potresti fare una query diretta a Supabase se preferisci)
|
||||
return state.allOperations
|
||||
.where(
|
||||
(op) =>
|
||||
op.batchUuid == currentBatch &&
|
||||
op.id != state.currentOperation!.id,
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
// --- GESTIONE DELLO STATO DEL FORM IN TEMPO REALE ---
|
||||
void updateOperationFields({
|
||||
String? customerId,
|
||||
String? customerDisplayName,
|
||||
String? type,
|
||||
String? providerId,
|
||||
String? providerDisplayName,
|
||||
String? subtype,
|
||||
String? description,
|
||||
DateTime? expirationDate,
|
||||
int? quantity,
|
||||
String? modelId,
|
||||
String? modelDisplayName,
|
||||
String? staffId,
|
||||
String? staffDisplayName,
|
||||
// Aggiungiamo questi flag per forzare la pulizia dei campi quando cambi tipo
|
||||
bool clearProvider = false,
|
||||
bool clearType = false,
|
||||
bool clearSubtype = false,
|
||||
bool clearDescription = false,
|
||||
bool clearExpiration = false,
|
||||
bool clearQuantity = false,
|
||||
bool clearModel = false,
|
||||
}) {
|
||||
if (state.currentOperation == null) return;
|
||||
|
||||
final current = state.currentOperation!;
|
||||
|
||||
// Creiamo il modello aggiornato
|
||||
// ATTENZIONE: adatta questa logica in base a come è scritto il tuo copyWith!
|
||||
int? newQuantity;
|
||||
if (clearQuantity) {
|
||||
newQuantity = 1;
|
||||
}
|
||||
if (quantity != null && quantity <= 0) {
|
||||
newQuantity = 0;
|
||||
}
|
||||
if (quantity != null && quantity > 0) {
|
||||
newQuantity = quantity;
|
||||
}
|
||||
final updated = current.copyWith(
|
||||
customerId: customerId,
|
||||
customerDisplayName: customerDisplayName,
|
||||
|
||||
// Se clearProvider è true, forziamo una stringa vuota (o null se il tuo modello lo supporta)
|
||||
providerId: clearProvider ? null : (providerId ?? current.providerId),
|
||||
providerDisplayName: clearProvider
|
||||
? null
|
||||
: (providerDisplayName ?? current.providerDisplayName),
|
||||
quantity: newQuantity,
|
||||
type: clearType ? null : (type ?? current.type),
|
||||
description: clearDescription
|
||||
? null
|
||||
: (description ?? current.description),
|
||||
subtype: clearSubtype ? null : (subtype ?? current.subtype),
|
||||
expirationDate: clearExpiration
|
||||
? null
|
||||
: (expirationDate ?? current.expirationDate),
|
||||
modelId: clearModel ? null : (modelId ?? current.modelId),
|
||||
modelDisplayName: clearModel
|
||||
? null
|
||||
: (modelDisplayName ?? current.modelDisplayName),
|
||||
staffId: staffId ?? current.staffId,
|
||||
staffDisplayName: staffDisplayName ?? current.staffDisplayName,
|
||||
);
|
||||
|
||||
emit(state.copyWith(currentOperation: updated));
|
||||
}
|
||||
|
||||
// Metodo di utilità per calcolare la data X mesi da oggi
|
||||
DateTime _calculateMonths(int months) {
|
||||
final now = DateTime.now();
|
||||
return DateTime(now.year, now.month + months, now.day);
|
||||
}
|
||||
|
||||
// Quando l'utente seleziona un tipo, impostiamo il default
|
||||
void setTypeWithSmartDefault(String type) {
|
||||
DateTime? defaultDate;
|
||||
|
||||
if (type == 'Energy') defaultDate = _calculateMonths(24);
|
||||
if (type == 'Fin') defaultDate = _calculateMonths(30);
|
||||
if (type == 'Entertainment') defaultDate = _calculateMonths(12);
|
||||
|
||||
updateOperationFields(
|
||||
type: type,
|
||||
expirationDate: defaultDate,
|
||||
clearProvider: true,
|
||||
clearSubtype: true,
|
||||
clearModel: true,
|
||||
clearQuantity: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user