k
This commit is contained in:
@@ -12,6 +12,7 @@ import 'package:flux/features/settings/document_sequence/models/document_sequenc
|
||||
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||
import 'package:flux/features/tickets/utils/ticket_shipping_pdf_service.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
|
||||
part 'ticket_shipping_state.dart';
|
||||
|
||||
@@ -163,12 +164,11 @@ class TicketShippingCubit extends Cubit<TicketShippingState> {
|
||||
pdfBytes: pdfBytes,
|
||||
newTicketStatus: newTicketStatus.value,
|
||||
);
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: TicketShippingStatus.success,
|
||||
pdfBytes: pdfBytes,
|
||||
),
|
||||
await Printing.layoutPdf(
|
||||
onLayout: (format) async => pdfBytes,
|
||||
name: 'DDT_${state.document.docNumber}.pdf',
|
||||
);
|
||||
emit(state.copyWith(status: TicketShippingStatus.success));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(isAutoNumber: false, errorMessage: e.toString()));
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,6 @@ class TicketShippingState extends Equatable {
|
||||
final TicketShippingStatus status;
|
||||
final ShipmentDocumentModel document;
|
||||
final List<TicketModel> tickets;
|
||||
final Uint8List? pdfBytes; // Per tenere il PDF in memoria dopo la generazione
|
||||
|
||||
// Dati di supporto per la UI
|
||||
final List<ProviderModel> availableProviders;
|
||||
@@ -22,7 +21,6 @@ class TicketShippingState extends Equatable {
|
||||
this.availableLocations = const [],
|
||||
this.isAutoNumber = true,
|
||||
this.errorMessage,
|
||||
this.pdfBytes,
|
||||
});
|
||||
|
||||
TicketShippingState copyWith({
|
||||
@@ -32,7 +30,6 @@ class TicketShippingState extends Equatable {
|
||||
List<ProviderLocationModel>? availableLocations,
|
||||
bool? isAutoNumber,
|
||||
String? errorMessage,
|
||||
Uint8List? pdfBytes,
|
||||
}) {
|
||||
return TicketShippingState(
|
||||
status: status ?? this.status,
|
||||
@@ -42,7 +39,6 @@ class TicketShippingState extends Equatable {
|
||||
availableLocations: availableLocations ?? this.availableLocations,
|
||||
isAutoNumber: isAutoNumber ?? this.isAutoNumber,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
pdfBytes: pdfBytes ?? this.pdfBytes,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +51,5 @@ class TicketShippingState extends Equatable {
|
||||
availableLocations,
|
||||
isAutoNumber,
|
||||
errorMessage,
|
||||
pdfBytes,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class TicketRepository {
|
||||
.select('''
|
||||
*,
|
||||
customer (*),
|
||||
shipment_document (*),
|
||||
created_by:staff_member!ticket_staff_id_fkey (*),
|
||||
assigned_to:staff_member!ticket_assigned_to_id_fkey (*),
|
||||
target_model:model!ticket_model_id_1_fkey (*),
|
||||
@@ -88,6 +89,7 @@ class TicketRepository {
|
||||
.select('''
|
||||
*,
|
||||
customer (*),
|
||||
shipment_document (*),
|
||||
created_by:staff_member!ticket_staff_id_fkey (*),
|
||||
assigned_to:staff_member!ticket_assigned_to_id_fkey (*),
|
||||
target_model:model!ticket_model_id_1_fkey (*),
|
||||
@@ -186,6 +188,7 @@ class TicketRepository {
|
||||
source_model:model!ticket_model_id_2_fkey (*),
|
||||
created_by:staff_member!ticket_staff_id_fkey (*),
|
||||
assigned_to:staff_member!ticket_assigned_to_id_fkey (*),
|
||||
shipment_document (*),
|
||||
''')
|
||||
.eq('id', ticketId)
|
||||
.single();
|
||||
|
||||
@@ -64,12 +64,19 @@ class TicketsShipmentRepository {
|
||||
documentData['storage_path'] = storagePath;
|
||||
|
||||
// 4. Inseriamo il Documento di Trasporto nel DB
|
||||
await _supabase.from('shipment_documents').insert(documentData);
|
||||
|
||||
final savedDocument = await _supabase
|
||||
.from('shipment_documents')
|
||||
.insert(documentData)
|
||||
.select('id')
|
||||
.single();
|
||||
final documentid = savedDocument['id'];
|
||||
// 5. Aggiorniamo lo stato di TUTTI i ticket inclusi nel DDT
|
||||
await _supabase
|
||||
.from('ticket')
|
||||
.update({'ticket_status': newTicketStatus})
|
||||
.update({
|
||||
'ticket_status': newTicketStatus,
|
||||
'shipment_document_id': documentid,
|
||||
})
|
||||
.inFilter('id', document.ticketIds);
|
||||
|
||||
// Restituiamo lo storagePath per usarlo subito nell'interfaccia se serve
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flux/core/utils/extensions.dart';
|
||||
import 'package:flux/features/customers/models/customer_model.dart';
|
||||
import 'package:flux/features/tickets/models/shipment_document_model.dart';
|
||||
|
||||
/// Enum per il tipo di ticket
|
||||
enum TicketType {
|
||||
@@ -111,6 +112,7 @@ class TicketModel extends Equatable {
|
||||
final DateTime? estimatedDeliveryAt;
|
||||
final TicketResult? ticketResult;
|
||||
final String? resolutionNotes;
|
||||
final String? shippingDocumentId;
|
||||
final CustomerModel? customer;
|
||||
final String? targetModelName;
|
||||
final String? sourceModelName;
|
||||
@@ -119,6 +121,8 @@ class TicketModel extends Equatable {
|
||||
final String? assignedToId;
|
||||
final String? assignedToName;
|
||||
final String? includedAccessories;
|
||||
final ShipmentDocumentModel?
|
||||
shippingDocument; // Per tenere in memoria i dati del DDT associato al ticket
|
||||
|
||||
const TicketModel({
|
||||
this.id,
|
||||
@@ -148,6 +152,7 @@ class TicketModel extends Equatable {
|
||||
this.estimatedDeliveryAt,
|
||||
this.ticketResult,
|
||||
this.resolutionNotes,
|
||||
this.shippingDocumentId,
|
||||
this.customer,
|
||||
this.targetModelName,
|
||||
this.sourceModelName,
|
||||
@@ -156,6 +161,7 @@ class TicketModel extends Equatable {
|
||||
this.assignedToId,
|
||||
this.assignedToName,
|
||||
this.includedAccessories,
|
||||
this.shippingDocument,
|
||||
});
|
||||
|
||||
/// Factory per creare un ticket vuoto (utile per i form di creazione)
|
||||
@@ -200,6 +206,7 @@ class TicketModel extends Equatable {
|
||||
DateTime? estimatedDeliveryAt,
|
||||
TicketResult? ticketResult,
|
||||
String? resolutionNotes,
|
||||
String? shippingDocumentId,
|
||||
CustomerModel? customer,
|
||||
String? targetModelName,
|
||||
String? sourceModelName,
|
||||
@@ -208,6 +215,7 @@ class TicketModel extends Equatable {
|
||||
String? assignedToId,
|
||||
String? assignedToName,
|
||||
String? includedAccessories,
|
||||
ShipmentDocumentModel? shippingDocument,
|
||||
}) {
|
||||
return TicketModel(
|
||||
id: id ?? this.id,
|
||||
@@ -238,6 +246,7 @@ class TicketModel extends Equatable {
|
||||
estimatedDeliveryAt: estimatedDeliveryAt ?? this.estimatedDeliveryAt,
|
||||
ticketResult: ticketResult ?? this.ticketResult,
|
||||
resolutionNotes: resolutionNotes ?? this.resolutionNotes,
|
||||
shippingDocumentId: shippingDocumentId ?? this.shippingDocumentId,
|
||||
customer: customer ?? this.customer,
|
||||
targetModelName: targetModelName ?? this.targetModelName,
|
||||
sourceModelName: sourceModelName ?? this.sourceModelName,
|
||||
@@ -246,6 +255,7 @@ class TicketModel extends Equatable {
|
||||
assignedToId: assignedToId ?? this.assignedToId,
|
||||
assignedToName: assignedToName ?? this.assignedToName,
|
||||
includedAccessories: includedAccessories ?? this.includedAccessories,
|
||||
shippingDocument: shippingDocument ?? this.shippingDocument,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -288,6 +298,7 @@ class TicketModel extends Equatable {
|
||||
: null,
|
||||
ticketResult: TicketResult.fromString(map['ticket_result'] as String?),
|
||||
resolutionNotes: map['resolution_notes'] as String?,
|
||||
shippingDocumentId: map['shipping_document_id'] as String?,
|
||||
customer: map['customer'] != null
|
||||
? CustomerModel.fromMap(map['customer'] as Map<String, dynamic>)
|
||||
: null,
|
||||
@@ -300,6 +311,11 @@ class TicketModel extends Equatable {
|
||||
assignedToId: map['assigned_to_id'] as String?,
|
||||
assignedToName: (map['assigned_to']?['name'] as String?)?.myFormat(),
|
||||
includedAccessories: map['included_accessories'] as String?,
|
||||
shippingDocument: map['shipping_document'] != null
|
||||
? ShipmentDocumentModel.fromMap(
|
||||
map['shipping_document'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -337,6 +353,7 @@ class TicketModel extends Equatable {
|
||||
if (ticketResult != null) 'ticket_result': ticketResult!.value,
|
||||
'resolution_notes': resolutionNotes,
|
||||
'included_accessories': includedAccessories,
|
||||
'shipping_document_id': shippingDocumentId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -376,5 +393,7 @@ class TicketModel extends Equatable {
|
||||
createdByName,
|
||||
assignedToId,
|
||||
assignedToName,
|
||||
shippingDocumentId,
|
||||
shippingDocument,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -103,8 +103,7 @@ class TicketList extends StatelessWidget {
|
||||
FilledButton.icon(
|
||||
onPressed: () async {
|
||||
// 1. Apriamo la modale e ASPETTIAMO il risultato (tipizzandolo come Record)
|
||||
final Uint8List? result =
|
||||
await showModalBottomSheet<Uint8List>(
|
||||
final bool? result = await showModalBottomSheet<bool?>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
@@ -124,11 +123,6 @@ class TicketList extends StatelessWidget {
|
||||
// 2. Se l'utente ha chiuso trascinando giù, result è null.
|
||||
// Se ha salvato con successo, result contiene il nostro Record!
|
||||
if (result != null && context.mounted) {
|
||||
await Printing.layoutPdf(
|
||||
onLayout: (format) async => result,
|
||||
name:
|
||||
'DDT_${DateTime.now().millisecondsSinceEpoch}.pdf',
|
||||
);
|
||||
// 5. Pulizia finale: Deselezioniamo tutti i ticket e ricarichiamo la lista
|
||||
context.read<TicketListCubit>().clearSelection();
|
||||
// (Se necessario, chiama il metodo per ricaricare la lista dei ticket dal DB)
|
||||
|
||||
@@ -110,6 +110,24 @@ class TicketListCard extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (ticket.shippingDocument != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
const Text('DDT'),
|
||||
const SizedBox(width: 4),
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
onTap: () {},
|
||||
child: const Icon(
|
||||
Icons.picture_as_pdf,
|
||||
color: Colors.redAccent,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
// IL BADGE DELLO STATO
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -123,6 +141,7 @@ class TicketListCard extends StatelessWidget {
|
||||
color: statusColor.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
||||
@@ -39,21 +39,7 @@ class _TicketShippingModalState extends State<TicketShippingModal> {
|
||||
child: BlocConsumer<TicketShippingCubit, TicketShippingState>(
|
||||
listener: (context, state) {
|
||||
if (state.status == TicketShippingStatus.success) {
|
||||
final provider = state.availableProviders.firstWhere(
|
||||
(p) => p.id == state.document.providerId,
|
||||
);
|
||||
final location = state.availableLocations.firstWhere(
|
||||
(l) => l.id == state.document.destinationLocationId,
|
||||
);
|
||||
|
||||
// Creiamo un Dart Record elegante e lo "spariamo" fuori
|
||||
final ddtData = (
|
||||
document: state.document,
|
||||
provider: provider,
|
||||
location: location,
|
||||
);
|
||||
|
||||
Navigator.pop(context, ddtData);
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
if (state.status == TicketShippingStatus.failure &&
|
||||
state.errorMessage != null) {
|
||||
|
||||
Reference in New Issue
Block a user