368 lines
12 KiB
Dart
368 lines
12 KiB
Dart
|
|
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) {
|
||
|
|
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 WarrantyType? warrantyType;
|
||
|
|
final String? publicNotes;
|
||
|
|
final String? internalNotes;
|
||
|
|
final int? referenceNumber;
|
||
|
|
final String? alternativePhoneNumber;
|
||
|
|
final bool hasCourtesyDevice;
|
||
|
|
final TicketType ticketType;
|
||
|
|
final TicketStatus ticketStatus;
|
||
|
|
final DateTime? estimatedDeliveryAt;
|
||
|
|
final TicketResult? ticketResult;
|
||
|
|
final String? resolutionNotes;
|
||
|
|
final String? legacyId;
|
||
|
|
final String? customerName;
|
||
|
|
final String? targetModelName;
|
||
|
|
final String? sourceModelName;
|
||
|
|
final String? createdById;
|
||
|
|
final String? createdByName;
|
||
|
|
final String? assignedToId;
|
||
|
|
final String? assignedToName;
|
||
|
|
final String? includedAccessories;
|
||
|
|
|
||
|
|
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.warrantyType,
|
||
|
|
this.publicNotes,
|
||
|
|
this.internalNotes,
|
||
|
|
this.referenceNumber,
|
||
|
|
this.alternativePhoneNumber,
|
||
|
|
this.hasCourtesyDevice = false,
|
||
|
|
required this.ticketType,
|
||
|
|
this.ticketStatus = TicketStatus.closed,
|
||
|
|
this.estimatedDeliveryAt,
|
||
|
|
this.ticketResult,
|
||
|
|
this.resolutionNotes,
|
||
|
|
this.legacyId,
|
||
|
|
this.customerName,
|
||
|
|
this.targetModelName,
|
||
|
|
this.sourceModelName,
|
||
|
|
this.createdById,
|
||
|
|
this.createdByName,
|
||
|
|
this.assignedToId,
|
||
|
|
this.assignedToName,
|
||
|
|
this.includedAccessories,
|
||
|
|
});
|
||
|
|
|
||
|
|
/// Factory per creare un ticket vuoto (utile per i form di creazione)
|
||
|
|
factory TicketModel.empty({String? companyId, String? storeId}) {
|
||
|
|
return TicketModel(
|
||
|
|
companyId: companyId ?? '',
|
||
|
|
storeId: storeId,
|
||
|
|
ticketType: TicketType.repair, // Valore di default
|
||
|
|
ticketStatus: 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,
|
||
|
|
WarrantyType? warrantyType,
|
||
|
|
String? publicNotes,
|
||
|
|
String? internalNotes,
|
||
|
|
int? referenceNumber,
|
||
|
|
String? alternativePhoneNumber,
|
||
|
|
bool? hasCourtesyDevice,
|
||
|
|
TicketType? ticketType,
|
||
|
|
TicketStatus? ticketStatus,
|
||
|
|
DateTime? estimatedDeliveryAt,
|
||
|
|
TicketResult? ticketResult,
|
||
|
|
String? resolutionNotes,
|
||
|
|
String? legacyId,
|
||
|
|
String? customerName,
|
||
|
|
String? targetModelName,
|
||
|
|
String? sourceModelName,
|
||
|
|
String? createdById,
|
||
|
|
String? createdByName,
|
||
|
|
String? assignedToId,
|
||
|
|
String? assignedToName,
|
||
|
|
String? includedAccessories,
|
||
|
|
}) {
|
||
|
|
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,
|
||
|
|
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,
|
||
|
|
ticketStatus: ticketStatus ?? this.ticketStatus,
|
||
|
|
estimatedDeliveryAt: estimatedDeliveryAt ?? this.estimatedDeliveryAt,
|
||
|
|
ticketResult: ticketResult ?? this.ticketResult,
|
||
|
|
resolutionNotes: resolutionNotes ?? this.resolutionNotes,
|
||
|
|
legacyId: legacyId ?? this.legacyId,
|
||
|
|
customerName: customerName ?? this.customerName,
|
||
|
|
targetModelName: targetModelName ?? this.targetModelName,
|
||
|
|
sourceModelName: sourceModelName ?? this.sourceModelName,
|
||
|
|
createdById: createdById ?? this.createdById,
|
||
|
|
createdByName: createdByName ?? this.createdByName,
|
||
|
|
assignedToId: assignedToId ?? this.assignedToId,
|
||
|
|
assignedToName: assignedToName ?? this.assignedToName,
|
||
|
|
includedAccessories: includedAccessories ?? this.includedAccessories,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 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? ?? '',
|
||
|
|
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),
|
||
|
|
ticketStatus: TicketStatus.fromString(map['ticket_status'] as String),
|
||
|
|
estimatedDeliveryAt: map['estimated_delivery_at'] != null
|
||
|
|
? DateTime.parse(map['estimated_delivery_at']).toLocal()
|
||
|
|
: null,
|
||
|
|
ticketResult: TicketResult.fromString(map['ticket_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(),
|
||
|
|
createdById: map['staff_id'] as String?,
|
||
|
|
createdByName: (map['staff']?['name'] as String?).myFormat(),
|
||
|
|
assignedToId: map['assigned_to_id'] as String?,
|
||
|
|
assignedToName: (map['assigned_to']?['name'] as String?).myFormat(),
|
||
|
|
includedAccessories: map['included_accessories'] as String?,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 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,
|
||
|
|
'created_by_id': createdById,
|
||
|
|
'warranty_type': warrantyType,
|
||
|
|
'public_notes': publicNotes,
|
||
|
|
'internal_notes': internalNotes,
|
||
|
|
'alternative_phone_number': alternativePhoneNumber,
|
||
|
|
'has_courtesy_device': hasCourtesyDevice,
|
||
|
|
'ticket_type': ticketType.value,
|
||
|
|
'ticket_status': ticketStatus.value,
|
||
|
|
if (estimatedDeliveryAt != null)
|
||
|
|
'estimated_delivery_at': estimatedDeliveryAt!.toUtc().toIso8601String(),
|
||
|
|
if (ticketResult != null) 'ticket_result': ticketResult!.value,
|
||
|
|
'resolution_notes': resolutionNotes,
|
||
|
|
'legacy_id': legacyId,
|
||
|
|
'included_accessories': includedAccessories,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
List<Object?> get props => [
|
||
|
|
id,
|
||
|
|
createdAt,
|
||
|
|
companyId,
|
||
|
|
storeId,
|
||
|
|
customerId,
|
||
|
|
targetModelId,
|
||
|
|
targetSn,
|
||
|
|
sourceModelId,
|
||
|
|
sourceSn,
|
||
|
|
customerPrice,
|
||
|
|
internalCost,
|
||
|
|
closedAt,
|
||
|
|
returnedAt,
|
||
|
|
request,
|
||
|
|
warrantyType,
|
||
|
|
publicNotes,
|
||
|
|
internalNotes,
|
||
|
|
referenceNumber,
|
||
|
|
alternativePhoneNumber,
|
||
|
|
hasCourtesyDevice,
|
||
|
|
ticketType,
|
||
|
|
ticketStatus,
|
||
|
|
estimatedDeliveryAt,
|
||
|
|
ticketResult,
|
||
|
|
resolutionNotes,
|
||
|
|
legacyId,
|
||
|
|
includedAccessories,
|
||
|
|
customerName,
|
||
|
|
targetModelName,
|
||
|
|
sourceModelName,
|
||
|
|
createdById,
|
||
|
|
createdByName,
|
||
|
|
assignedToId,
|
||
|
|
assignedToName,
|
||
|
|
];
|
||
|
|
}
|