Files
flux/lib/features/tickets/models/ticket_model.dart

399 lines
13 KiB
Dart
Raw Permalink Normal View History

import 'package:equatable/equatable.dart';
import 'package:flux/core/utils/extensions.dart';
2026-05-12 11:14:48 +02:00
import 'package:flux/features/customers/models/customer_model.dart';
import 'package:flux/features/tickets/models/shipping_document_model.dart';
/// Enum per il tipo di ticket
enum TicketType {
repair('repair', 'Riparazione'),
2026-05-12 11:14:48 +02:00
softwareSetup('software_setup', 'Impost. software'),
dataTransfer('data_transfer', 'Trasf. dati'),
operationTicket('operation_ticket', 'Ticket 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'),
2026-05-14 12:07:05 +02:00
waitingForReturn('waiting_for_return', 'In attesa di ritorno'),
waitingForQuote('waiting_for_quote', 'In attesa di preventivo'),
waitingForCustomer('waiting_for_customer', 'In attesa del cliente');
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;
2026-05-13 19:24:25 +02:00
final String? targetPassword;
final String? sourceModelId;
final String? sourceSn;
2026-05-13 19:24:25 +02:00
final String? sourcePassword;
final double customerPrice;
final double internalCost;
final DateTime? closedAt;
final DateTime? returnedAt;
final String request;
final WarrantyType? warrantyType;
final String? publicNotes;
final String? internalNotes;
2026-05-10 14:09:57 +02:00
final String? referenceId;
final String? alternativePhoneNumber;
final bool hasCourtesyDevice;
final TicketType ticketType;
final TicketStatus ticketStatus;
final DateTime? estimatedDeliveryAt;
final TicketResult? ticketResult;
final String? resolutionNotes;
2026-05-18 08:31:39 +02:00
final String? shippingDocumentId;
2026-05-12 11:14:48 +02:00
final CustomerModel? customer;
final String? targetModelName;
final String? sourceModelName;
final String? createdById;
final String? createdByName;
final String? assignedToId;
final String? assignedToName;
final String? includedAccessories;
final ShippingDocumentModel? shippingDocument;
const TicketModel({
this.id,
this.createdAt,
required this.companyId,
this.storeId,
this.customerId,
this.targetModelId,
this.targetSn,
2026-05-13 19:24:25 +02:00
this.targetPassword,
this.sourceModelId,
this.sourceSn,
2026-05-13 19:24:25 +02:00
this.sourcePassword,
this.customerPrice = 0.0,
this.internalCost = 0.0,
this.closedAt,
this.returnedAt,
this.request = '',
this.warrantyType,
this.publicNotes,
this.internalNotes,
2026-05-10 14:09:57 +02:00
this.referenceId,
this.alternativePhoneNumber,
this.hasCourtesyDevice = false,
required this.ticketType,
this.ticketStatus = TicketStatus.closed,
this.estimatedDeliveryAt,
this.ticketResult,
this.resolutionNotes,
2026-05-18 08:31:39 +02:00
this.shippingDocumentId,
2026-05-12 11:14:48 +02:00
this.customer,
this.targetModelName,
this.sourceModelName,
this.createdById,
this.createdByName,
this.assignedToId,
this.assignedToName,
this.includedAccessories,
2026-05-18 08:31:39 +02:00
this.shippingDocument,
});
/// 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,
2026-05-13 19:24:25 +02:00
String? targetPassword,
String? sourceModelId,
String? sourceSn,
2026-05-13 19:24:25 +02:00
String? sourcePassword,
double? customerPrice,
double? internalCost,
DateTime? closedAt,
DateTime? returnedAt,
String? request,
WarrantyType? warrantyType,
String? publicNotes,
String? internalNotes,
2026-05-10 14:09:57 +02:00
String? referenceId,
String? alternativePhoneNumber,
bool? hasCourtesyDevice,
TicketType? ticketType,
TicketStatus? ticketStatus,
DateTime? estimatedDeliveryAt,
TicketResult? ticketResult,
String? resolutionNotes,
2026-05-18 08:31:39 +02:00
String? shippingDocumentId,
2026-05-12 11:14:48 +02:00
CustomerModel? customer,
String? targetModelName,
String? sourceModelName,
String? createdById,
String? createdByName,
String? assignedToId,
String? assignedToName,
String? includedAccessories,
ShippingDocumentModel? shippingDocument,
}) {
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,
2026-05-13 19:24:25 +02:00
targetPassword: targetPassword ?? this.targetPassword,
sourceModelId: sourceModelId ?? this.sourceModelId,
sourceSn: sourceSn ?? this.sourceSn,
2026-05-13 19:24:25 +02:00
sourcePassword: sourcePassword ?? this.sourcePassword,
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,
2026-05-10 14:09:57 +02:00
referenceId: referenceId ?? this.referenceId,
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,
2026-05-18 08:31:39 +02:00
shippingDocumentId: shippingDocumentId ?? this.shippingDocumentId,
2026-05-12 11:14:48 +02:00
customer: customer ?? this.customer,
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,
2026-05-18 08:31:39 +02:00
shippingDocument: shippingDocument ?? this.shippingDocument,
);
}
/// 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?,
2026-05-13 19:24:25 +02:00
targetPassword: map['target_password'] as String?,
sourcePassword: map['source_password'] 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?,
2026-05-10 14:09:57 +02:00
referenceId: map['reference_id'] as String?,
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?,
2026-05-18 08:31:39 +02:00
shippingDocumentId: map['shipping_document_id'] as String?,
2026-05-12 11:14:48 +02:00
customer: map['customer'] != null
? CustomerModel.fromMap(map['customer'] as Map<String, dynamic>)
: null,
targetModelName: (map['target_model']?['name_with_brand'] as String?)
?.myFormat(),
sourceModelName: (map['source_model']?['name_with_brand'] as String?)
?.myFormat(),
2026-05-14 19:22:02 +02:00
createdById: map['created_by_id'] as String?,
2026-05-14 12:07:05 +02:00
createdByName: (map['created_by']?['name'] as String?)?.myFormat(),
assignedToId: map['assigned_to_id'] as String?,
2026-05-14 12:07:05 +02:00
assignedToName: (map['assigned_to']?['name'] as String?)?.myFormat(),
includedAccessories: map['included_accessories'] as String?,
2026-05-18 08:31:39 +02:00
shippingDocument: map['shipping_document'] != null
? ShippingDocumentModel.fromMap(
2026-05-18 08:31:39 +02:00
map['shipping_document'] as Map<String, dynamic>,
)
: null,
);
}
/// 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,
2026-05-13 19:24:25 +02:00
'target_password': targetPassword,
'source_password': sourcePassword,
'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,
2026-05-14 12:07:05 +02:00
'warranty_type': warrantyType?.value ?? WarrantyType.noWarranty.value,
'public_notes': publicNotes,
'internal_notes': internalNotes,
2026-05-10 14:09:57 +02:00
'reference_id': referenceId,
'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,
'included_accessories': includedAccessories,
2026-05-18 08:31:39 +02:00
'shipping_document_id': shippingDocumentId,
};
}
@override
List<Object?> get props => [
id,
createdAt,
companyId,
storeId,
customerId,
targetModelId,
targetSn,
sourceModelId,
sourceSn,
2026-05-13 19:24:25 +02:00
targetPassword,
sourcePassword,
customerPrice,
internalCost,
closedAt,
returnedAt,
request,
warrantyType,
publicNotes,
internalNotes,
alternativePhoneNumber,
hasCourtesyDevice,
ticketType,
ticketStatus,
estimatedDeliveryAt,
ticketResult,
resolutionNotes,
includedAccessories,
2026-05-12 11:14:48 +02:00
customer,
targetModelName,
sourceModelName,
createdById,
createdByName,
assignedToId,
assignedToName,
2026-05-18 08:31:39 +02:00
shippingDocumentId,
shippingDocument,
];
}