345
lib/features/tickets/models/ticket_model.dart
Normal file
345
lib/features/tickets/models/ticket_model.dart
Normal file
@@ -0,0 +1,345 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flux/core/utils/extensions.dart';
|
||||
|
||||
/// Enum per il tipo di ticket
|
||||
enum TicketType {
|
||||
repair('repair', 'Riparazione'),
|
||||
softwareSetup('software_setup', 'Setup software'),
|
||||
dataTransfer('data_transfer', 'Trasferimento dati'),
|
||||
operationTicket('operation_ticket', 'Ticket di operazione'),
|
||||
other('other', 'Altro');
|
||||
|
||||
final String value;
|
||||
final String displayValue;
|
||||
const TicketType(this.value, this.displayValue);
|
||||
|
||||
static TicketType fromString(String val) {
|
||||
return TicketType.values.firstWhere(
|
||||
(e) => e.value == val,
|
||||
orElse: () => TicketType.other,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum per lo stato del ticket
|
||||
enum TicketStatus {
|
||||
open('open', 'Aperto'),
|
||||
inProgress('in_progress', 'In corso'),
|
||||
waitingForParts('waiting_for_parts', 'In attesa di ricambi'),
|
||||
ready('ready', 'Pronto'),
|
||||
closed('closed', 'Chiuso'),
|
||||
waitingForShipping('waiting_for_shipping', 'In attesa di spedire'),
|
||||
waitingForReturn('waiting_for_return', 'In attesa di ritorno');
|
||||
|
||||
final String value;
|
||||
final String displayValue;
|
||||
const TicketStatus(this.value, this.displayValue);
|
||||
|
||||
static TicketStatus? fromString(String? val) {
|
||||
if (val == null) return null;
|
||||
return TicketStatus.values.firstWhere(
|
||||
(e) => e.value == val,
|
||||
orElse: () => TicketStatus.open,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum per il risultato del ticket (OK / KO)
|
||||
enum TicketResult {
|
||||
success('success', 'Risolto (OK)'),
|
||||
failure('failure', 'Non Risolto (KO)');
|
||||
|
||||
final String value;
|
||||
final String displayValue;
|
||||
const TicketResult(this.value, this.displayValue);
|
||||
|
||||
static TicketResult? fromString(String? val) {
|
||||
if (val == null) return null;
|
||||
return TicketResult.values.firstWhere(
|
||||
(e) => e.value == val,
|
||||
orElse: () => TicketResult.success,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum per il tipo di garanzia
|
||||
enum WarrantyType {
|
||||
manufacturerWarranty('manufacturer_warranty', 'Garanzia produttore'),
|
||||
providerWarranty('provider_warranty', 'Garanzia gestore'),
|
||||
internalWarranty('internal_warranty', 'Garanzia interna'),
|
||||
noWarranty('no_warranty', 'Fuori garanzia');
|
||||
|
||||
final String value;
|
||||
final String displayValue;
|
||||
const WarrantyType(this.value, this.displayValue);
|
||||
|
||||
static WarrantyType? fromString(String? val) {
|
||||
return WarrantyType.values.firstWhere(
|
||||
(e) => e.value == val,
|
||||
orElse: () => WarrantyType.noWarranty,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TicketModel extends Equatable {
|
||||
final String? id; // Null se non ancora salvato
|
||||
final DateTime? createdAt;
|
||||
final String companyId;
|
||||
final String? storeId;
|
||||
final String? customerId;
|
||||
final String? targetModelId;
|
||||
final String? targetSn;
|
||||
final String? sourceModelId;
|
||||
final String? sourceSn;
|
||||
final double customerPrice;
|
||||
final double internalCost;
|
||||
final DateTime? closedAt;
|
||||
final DateTime? returnedAt;
|
||||
final String request;
|
||||
final String? staffId;
|
||||
final WarrantyType? warrantyType;
|
||||
final String? publicNotes;
|
||||
final String? internalNotes;
|
||||
final int? referenceNumber;
|
||||
final String? alternativePhoneNumber;
|
||||
final bool hasCourtesyDevice;
|
||||
final TicketType ticketType;
|
||||
final TicketStatus? status;
|
||||
final DateTime? estimatedDeliveryAt;
|
||||
final TicketResult? result;
|
||||
final String? resolutionNotes;
|
||||
final String? legacyId;
|
||||
final String? customerName;
|
||||
final String? targetModelName;
|
||||
final String? sourceModelName;
|
||||
final String? staffName;
|
||||
|
||||
const TicketModel({
|
||||
this.id,
|
||||
this.createdAt,
|
||||
required this.companyId,
|
||||
this.storeId,
|
||||
this.customerId,
|
||||
this.targetModelId,
|
||||
this.targetSn,
|
||||
this.sourceModelId,
|
||||
this.sourceSn,
|
||||
this.customerPrice = 0.0,
|
||||
this.internalCost = 0.0,
|
||||
this.closedAt,
|
||||
this.returnedAt,
|
||||
this.request = '',
|
||||
this.staffId,
|
||||
this.warrantyType,
|
||||
this.publicNotes,
|
||||
this.internalNotes,
|
||||
this.referenceNumber,
|
||||
this.alternativePhoneNumber,
|
||||
this.hasCourtesyDevice = false,
|
||||
required this.ticketType,
|
||||
this.status,
|
||||
this.estimatedDeliveryAt,
|
||||
this.result,
|
||||
this.resolutionNotes,
|
||||
this.legacyId,
|
||||
this.customerName,
|
||||
this.targetModelName,
|
||||
this.sourceModelName,
|
||||
this.staffName,
|
||||
});
|
||||
|
||||
/// Factory per creare un ticket vuoto (utile per i form di creazione)
|
||||
factory TicketModel.empty({required String companyId, String? storeId}) {
|
||||
return TicketModel(
|
||||
companyId: companyId,
|
||||
storeId: storeId,
|
||||
ticketType: TicketType.repair, // Valore di default
|
||||
status: TicketStatus.open,
|
||||
customerPrice: 0.0,
|
||||
internalCost: 0.0,
|
||||
hasCourtesyDevice: false,
|
||||
request: '',
|
||||
);
|
||||
}
|
||||
|
||||
TicketModel copyWith({
|
||||
String? id,
|
||||
DateTime? createdAt,
|
||||
String? companyId,
|
||||
String? storeId,
|
||||
String? customerId,
|
||||
String? targetModelId,
|
||||
String? targetSn,
|
||||
String? sourceModelId,
|
||||
String? sourceSn,
|
||||
double? customerPrice,
|
||||
double? internalCost,
|
||||
DateTime? closedAt,
|
||||
DateTime? returnedAt,
|
||||
String? request,
|
||||
String? staffId,
|
||||
WarrantyType? warrantyType,
|
||||
String? publicNotes,
|
||||
String? internalNotes,
|
||||
int? referenceNumber,
|
||||
String? alternativePhoneNumber,
|
||||
bool? hasCourtesyDevice,
|
||||
TicketType? ticketType,
|
||||
TicketStatus? status,
|
||||
DateTime? estimatedDeliveryAt,
|
||||
TicketResult? result,
|
||||
String? resolutionNotes,
|
||||
String? legacyId,
|
||||
String? customerName,
|
||||
String? targetModelName,
|
||||
String? sourceModelName,
|
||||
String? staffName,
|
||||
}) {
|
||||
return TicketModel(
|
||||
id: id ?? this.id,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
companyId: companyId ?? this.companyId,
|
||||
storeId: storeId ?? this.storeId,
|
||||
customerId: customerId ?? this.customerId,
|
||||
targetModelId: targetModelId ?? this.targetModelId,
|
||||
targetSn: targetSn ?? this.targetSn,
|
||||
sourceModelId: sourceModelId ?? this.sourceModelId,
|
||||
sourceSn: sourceSn ?? this.sourceSn,
|
||||
customerPrice: customerPrice ?? this.customerPrice,
|
||||
internalCost: internalCost ?? this.internalCost,
|
||||
closedAt: closedAt ?? this.closedAt,
|
||||
returnedAt: returnedAt ?? this.returnedAt,
|
||||
request: request ?? this.request,
|
||||
staffId: staffId ?? this.staffId,
|
||||
warrantyType: warrantyType ?? this.warrantyType,
|
||||
publicNotes: publicNotes ?? this.publicNotes,
|
||||
internalNotes: internalNotes ?? this.internalNotes,
|
||||
referenceNumber: referenceNumber ?? this.referenceNumber,
|
||||
alternativePhoneNumber:
|
||||
alternativePhoneNumber ?? this.alternativePhoneNumber,
|
||||
hasCourtesyDevice: hasCourtesyDevice ?? this.hasCourtesyDevice,
|
||||
ticketType: ticketType ?? this.ticketType,
|
||||
status: status ?? this.status,
|
||||
estimatedDeliveryAt: estimatedDeliveryAt ?? this.estimatedDeliveryAt,
|
||||
result: result ?? this.result,
|
||||
resolutionNotes: resolutionNotes ?? this.resolutionNotes,
|
||||
legacyId: legacyId ?? this.legacyId,
|
||||
customerName: customerName ?? this.customerName,
|
||||
targetModelName: targetModelName ?? this.targetModelName,
|
||||
sourceModelName: sourceModelName ?? this.sourceModelName,
|
||||
staffName: staffName ?? this.staffName,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deserializzazione da Supabase
|
||||
factory TicketModel.fromMap(Map<String, dynamic> map) {
|
||||
return TicketModel(
|
||||
id: map['id'] as String,
|
||||
createdAt: map['created_at'] != null
|
||||
? DateTime.parse(map['created_at']).toLocal()
|
||||
: null,
|
||||
companyId: map['company_id'] as String,
|
||||
storeId: map['store_id'] as String?,
|
||||
customerId: map['customer_id'] as String?,
|
||||
targetModelId: map['target_model_id'] as String?,
|
||||
targetSn: map['target_sn'] as String?,
|
||||
sourceModelId: map['source_model_id'] as String?,
|
||||
sourceSn: map['source_sn'] as String?,
|
||||
// Fix per i field numerici di Postgres che potrebbero arrivare come int o double
|
||||
customerPrice: (map['customer_price'] as num?)?.toDouble() ?? 0.0,
|
||||
internalCost: (map['internal_cost'] as num?)?.toDouble() ?? 0.0,
|
||||
closedAt: map['closed_at'] != null
|
||||
? DateTime.parse(map['closed_at']).toLocal()
|
||||
: null,
|
||||
returnedAt: map['returned_at'] != null
|
||||
? DateTime.parse(map['returned_at']).toLocal()
|
||||
: null,
|
||||
request: map['request'] as String? ?? '',
|
||||
staffId: map['staff_id'] as String?,
|
||||
warrantyType: WarrantyType.fromString(map['warranty_type'] as String?),
|
||||
publicNotes: map['public_notes'] as String?,
|
||||
internalNotes: map['internal_notes'] as String?,
|
||||
referenceNumber: map['reference_number'] as int?,
|
||||
alternativePhoneNumber: map['alternative_phone_number'] as String?,
|
||||
hasCourtesyDevice: map['has_courtesy_device'] as bool? ?? false,
|
||||
ticketType: TicketType.fromString(map['ticket_type'] as String),
|
||||
status: TicketStatus.fromString(map['status'] as String?),
|
||||
estimatedDeliveryAt: map['estimated_delivery_at'] != null
|
||||
? DateTime.parse(map['estimated_delivery_at']).toLocal()
|
||||
: null,
|
||||
result: TicketResult.fromString(map['result'] as String?),
|
||||
resolutionNotes: map['resolution_notes'] as String?,
|
||||
legacyId: map['legacy_id'] as String?,
|
||||
customerName: (map['customer']?['name'] as String?).myFormat(),
|
||||
targetModelName: (map['target_model']?['name_with_brand'] as String?)
|
||||
?.myFormat(),
|
||||
sourceModelName: (map['source_model']?['name_with_brand'] as String?)
|
||||
?.myFormat(),
|
||||
staffName: (map['staff']?['name'] as String?).myFormat(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Serializzazione per Supabase
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
if (id != null) 'id': id,
|
||||
'company_id': companyId,
|
||||
'store_id': storeId,
|
||||
'customer_id': customerId,
|
||||
'target_model_id': targetModelId,
|
||||
'target_sn': targetSn,
|
||||
'source_model_id': sourceModelId,
|
||||
'source_sn': sourceSn,
|
||||
'customer_price': customerPrice,
|
||||
'internal_cost': internalCost,
|
||||
if (closedAt != null) 'closed_at': closedAt!.toUtc().toIso8601String(),
|
||||
if (returnedAt != null)
|
||||
'returned_at': returnedAt!.toUtc().toIso8601String(),
|
||||
'request': request,
|
||||
'staff_id': staffId,
|
||||
'warranty_type': warrantyType,
|
||||
'public_notes': publicNotes,
|
||||
'internal_notes': internalNotes,
|
||||
'alternative_phone_number': alternativePhoneNumber,
|
||||
'has_courtesy_device': hasCourtesyDevice,
|
||||
'ticket_type': ticketType.value,
|
||||
if (status != null) 'status': status!.value,
|
||||
if (estimatedDeliveryAt != null)
|
||||
'estimated_delivery_at': estimatedDeliveryAt!.toUtc().toIso8601String(),
|
||||
if (result != null) 'result': result!.value,
|
||||
'resolution_notes': resolutionNotes,
|
||||
'legacy_id': legacyId,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
createdAt,
|
||||
companyId,
|
||||
storeId,
|
||||
customerId,
|
||||
targetModelId,
|
||||
targetSn,
|
||||
sourceModelId,
|
||||
sourceSn,
|
||||
customerPrice,
|
||||
internalCost,
|
||||
closedAt,
|
||||
returnedAt,
|
||||
request,
|
||||
staffId,
|
||||
warrantyType,
|
||||
publicNotes,
|
||||
internalNotes,
|
||||
referenceNumber,
|
||||
alternativePhoneNumber,
|
||||
hasCourtesyDevice,
|
||||
ticketType,
|
||||
status,
|
||||
estimatedDeliveryAt,
|
||||
result,
|
||||
resolutionNotes,
|
||||
legacyId,
|
||||
];
|
||||
}
|
||||
43
lib/features/tickets/models/ticket_status_extension.dart
Normal file
43
lib/features/tickets/models/ticket_status_extension.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||
|
||||
extension TicketStatusVisuals on TicketStatus {
|
||||
Color get color {
|
||||
switch (this) {
|
||||
case TicketStatus.open:
|
||||
return Colors.blueGrey;
|
||||
case TicketStatus.waitingForParts:
|
||||
return Colors.amber.shade700;
|
||||
case TicketStatus.inProgress:
|
||||
return Colors.blue;
|
||||
case TicketStatus.waitingForShipping:
|
||||
// Il tuo rosa storico!
|
||||
return Colors.pinkAccent;
|
||||
case TicketStatus.waitingForReturn:
|
||||
return Colors.purpleAccent;
|
||||
case TicketStatus.ready:
|
||||
return Colors.green;
|
||||
case TicketStatus.closed:
|
||||
return Colors.grey.shade400;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch (this) {
|
||||
case TicketStatus.open:
|
||||
return Icons.inbox;
|
||||
case TicketStatus.waitingForParts:
|
||||
return Icons.hourglass_empty;
|
||||
case TicketStatus.inProgress:
|
||||
return Icons.build;
|
||||
case TicketStatus.waitingForShipping:
|
||||
return Icons.local_shipping_outlined;
|
||||
case TicketStatus.waitingForReturn:
|
||||
return Icons.undo;
|
||||
case TicketStatus.ready:
|
||||
return Icons.check_circle_outline;
|
||||
case TicketStatus.closed:
|
||||
return Icons.lock_outline;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user