refactor shipping attachments and changed shipment to shipping for coherence
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
import 'package:flux/features/tickets/data/tickets_shipment_repository.dart';
|
||||
import 'package:flux/features/tickets/models/shipment_document_model.dart';
|
||||
import 'package:flux/features/tickets/data/tickets_shipping_repository.dart';
|
||||
import 'package:flux/features/tickets/models/shipping_document_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_location_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_model.dart';
|
||||
import 'package:flux/features/settings/document_sequence/data/document_sequence_repository.dart';
|
||||
@@ -16,15 +15,15 @@ import 'package:printing/printing.dart';
|
||||
part 'ticket_shipping_state.dart';
|
||||
|
||||
class TicketShippingCubit extends Cubit<TicketShippingState> {
|
||||
final TicketsShipmentRepository _repository =
|
||||
GetIt.I<TicketsShipmentRepository>();
|
||||
final TicketsShippingRepository _repository =
|
||||
GetIt.I<TicketsShippingRepository>();
|
||||
final DocumentSequenceRepository _sequenceRepository =
|
||||
GetIt.I<DocumentSequenceRepository>();
|
||||
TicketShippingCubit({required List<TicketModel> tickets})
|
||||
: super(
|
||||
TicketShippingState(
|
||||
// Inizializziamo il modello direttamente nello stato!
|
||||
document: ShipmentDocumentModel(
|
||||
document: ShippingDocumentModel(
|
||||
companyId: GetIt.I.get<SessionCubit>().state.company!.id!,
|
||||
ticketIds: tickets.map((t) => t.id!).toList(),
|
||||
providerId: '', // Sarà riempito alla selezione
|
||||
|
||||
@@ -4,7 +4,7 @@ enum TicketShippingStatus { initial, loading, success, failure }
|
||||
|
||||
class TicketShippingState extends Equatable {
|
||||
final TicketShippingStatus status;
|
||||
final ShipmentDocumentModel document;
|
||||
final ShippingDocumentModel document;
|
||||
final List<TicketModel> tickets;
|
||||
|
||||
// Dati di supporto per la UI
|
||||
@@ -25,7 +25,7 @@ class TicketShippingState extends Equatable {
|
||||
|
||||
TicketShippingState copyWith({
|
||||
TicketShippingStatus? status,
|
||||
ShipmentDocumentModel? document,
|
||||
ShippingDocumentModel? document,
|
||||
List<ProviderModel>? availableProviders,
|
||||
List<ProviderLocationModel>? availableLocations,
|
||||
bool? isAutoNumber,
|
||||
|
||||
@@ -31,7 +31,7 @@ class TicketRepository {
|
||||
.select('''
|
||||
*,
|
||||
customer (*),
|
||||
shipment_document (*),
|
||||
shipment_document (*, attachments (*)), -- BAM! Deep Join
|
||||
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 (*),
|
||||
@@ -89,7 +89,7 @@ class TicketRepository {
|
||||
.select('''
|
||||
*,
|
||||
customer (*),
|
||||
shipment_document (*),
|
||||
shipment_document (*, attachments (*)),
|
||||
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 (*),
|
||||
@@ -188,7 +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 (*),
|
||||
shipment_document (*, attachments (*)),
|
||||
''')
|
||||
.eq('id', ticketId)
|
||||
.single();
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flux/features/tickets/models/shipment_document_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_role.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
class TicketsShipmentRepository {
|
||||
final _supabase = GetIt.I.get<SupabaseClient>();
|
||||
|
||||
Future<List<ProviderModel>> fetchRepairCenters() async {
|
||||
try {
|
||||
final response = await _supabase
|
||||
.from('provider')
|
||||
.select('*, provider_locations (*)')
|
||||
.eq('is_active', true)
|
||||
.order('name');
|
||||
|
||||
final allProviders = (response as List)
|
||||
.map((row) => ProviderModel.fromMap(row as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
// Filtriamo lato client per prendere SOLO i repairCenter
|
||||
return allProviders
|
||||
.where((p) => p.roles.contains(ProviderRole.repairCenter))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw ('Errore caricamento laboratori: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Salva il DDT nel DB, fa l'upload del PDF nello Storage e aggiorna il path
|
||||
Future<String> createShipmentWithPdf({
|
||||
required ShipmentDocumentModel document,
|
||||
required Uint8List pdfBytes,
|
||||
required String newTicketStatus,
|
||||
}) async {
|
||||
try {
|
||||
// 1. Definiamo un percorso unico e ordinato per il file nello Storage
|
||||
// Struttura: company_id / ddt / anno / numero_ddt.pdf
|
||||
final year = document.docDate.year;
|
||||
final cleanedDocNumber = document.docNumber
|
||||
.replaceAll('/', '_')
|
||||
.replaceAll(' ', '_');
|
||||
final storagePath =
|
||||
'${document.companyId}/ddt/$year/$cleanedDocNumber.pdf';
|
||||
|
||||
// 2. Facciamo l'upload dei bytes grezzi nel bucket 'company_documents'
|
||||
await _supabase.storage
|
||||
.from('company_documents')
|
||||
.uploadBinary(
|
||||
storagePath,
|
||||
pdfBytes,
|
||||
fileOptions: const FileOptions(
|
||||
contentType: 'application/pdf',
|
||||
upsert: true,
|
||||
),
|
||||
);
|
||||
|
||||
// 3. Creiamo la mappa del documento includendo il percorso dello storage
|
||||
final documentData = document.toMap();
|
||||
documentData['storage_path'] = storagePath;
|
||||
|
||||
// 4. Inseriamo il Documento di Trasporto nel DB
|
||||
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,
|
||||
'shipment_document_id': documentid,
|
||||
})
|
||||
.inFilter('id', document.ticketIds);
|
||||
|
||||
// Restituiamo lo storagePath per usarlo subito nell'interfaccia se serve
|
||||
return storagePath;
|
||||
} catch (e) {
|
||||
throw ('Errore durante il salvataggio e upload della spedizione: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
135
lib/features/tickets/data/tickets_shipping_repository.dart
Normal file
135
lib/features/tickets/data/tickets_shipping_repository.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||
import 'package:flux/features/attachments/data/attachments_repository.dart';
|
||||
import 'package:flux/features/tickets/models/shipping_document_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_role.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart' hide Bucket;
|
||||
|
||||
class TicketsShippingRepository {
|
||||
final _supabase = GetIt.I.get<SupabaseClient>();
|
||||
final _companyId = GetIt.I.get<SessionCubit>().state.company!.id!;
|
||||
|
||||
Future<List<ProviderModel>> fetchRepairCenters() async {
|
||||
try {
|
||||
final response = await _supabase
|
||||
.from('provider')
|
||||
.select('*, provider_locations (*)')
|
||||
.eq('is_active', true)
|
||||
.order('name');
|
||||
|
||||
final allProviders = (response as List)
|
||||
.map((row) => ProviderModel.fromMap(row as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
// Filtriamo lato client per prendere SOLO i repairCenter
|
||||
return allProviders
|
||||
.where((p) => p.roles.contains(ProviderRole.repairCenter))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw ('Errore caricamento laboratori: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ShippingDocumentModel>> fetchShipmentDocumentsForTicket(
|
||||
String ticketId,
|
||||
) async {
|
||||
try {
|
||||
final response = await _supabase
|
||||
.from('shipping_documents')
|
||||
.select('*, attachments (*)')
|
||||
.contains('ticket_ids', [ticketId]);
|
||||
|
||||
return (response as List)
|
||||
.map(
|
||||
(row) => ShippingDocumentModel.fromMap(row as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw ('Errore caricamento documenti di trasporto: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ShippingDocumentModel>> fetchCompanyShipmentDocuments() async {
|
||||
try {
|
||||
final response = await _supabase
|
||||
.from('shipping_documents')
|
||||
.select('*, attachments (*)')
|
||||
.eq('company_id', _companyId);
|
||||
|
||||
return (response as List)
|
||||
.map(
|
||||
(row) => ShippingDocumentModel.fromMap(row as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw ('Errore caricamento documenti di trasporto aziendali: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<ShippingDocumentModel> fetchShipmentDocumentById(
|
||||
String documentId,
|
||||
) async {
|
||||
try {
|
||||
final response = await _supabase
|
||||
.from('shipping_documents')
|
||||
.select('*, attachments (*)')
|
||||
.eq('id', documentId)
|
||||
.single();
|
||||
|
||||
return ShippingDocumentModel.fromMap(response);
|
||||
} catch (e) {
|
||||
throw ('Errore caricamento documento di trasporto: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Salva il DDT nel DB, carica il PDF nello Storage e lo registra come Attachment ufficiale
|
||||
Future<void> createShipmentWithPdf({
|
||||
required ShippingDocumentModel document,
|
||||
required Uint8List pdfBytes,
|
||||
required String newTicketStatus,
|
||||
}) async {
|
||||
try {
|
||||
final attachmentsRepo = GetIt.I.get<AttachmentsRepository>();
|
||||
|
||||
// 1. Inseriamo prima il Documento di Trasporto (DDT) su Postgres
|
||||
// Il modello iniziale non ha ancora gli allegati popolati, quindi passiamo il toMap standard
|
||||
final savedDocument = await _supabase
|
||||
.from('shipping_documents')
|
||||
.insert(document.toMap())
|
||||
.select('id')
|
||||
.single();
|
||||
|
||||
final documentId = savedDocument['id'] as String;
|
||||
|
||||
// 2. Prepariamo il nome del file PDF (es: DDT_123_2026.pdf)
|
||||
final fileName =
|
||||
'DDT_${document.docNumber.replaceAll('/', '_').replaceAll(' ', '_')}.pdf';
|
||||
|
||||
// 3. IL TOCCO MASTER: Delegiamo l'upload e la registrazione nel DB all'AttachmentsRepository
|
||||
// Questo creerà la riga nella tabella degli allegati agganciando lo 'shipping_document_id'
|
||||
await attachmentsRepo.uploadAndRegisterFile(
|
||||
parentId: documentId,
|
||||
parentType: AttachmentParentType.shippingDocument, // Il tuo nuovo enum!
|
||||
companyId: document.companyId,
|
||||
bucket: Bucket.companyDocuments,
|
||||
rawBytes: pdfBytes,
|
||||
rawFileName: fileName,
|
||||
);
|
||||
|
||||
// 4. Aggiorniamo lo stato di TUTTI i ticket inclusi nel DDT e li colleghiamo all'ID del DDT
|
||||
await _supabase
|
||||
.from('ticket')
|
||||
.update({
|
||||
'ticket_status': newTicketStatus,
|
||||
'shipment_document_id': documentId,
|
||||
})
|
||||
.inFilter('id', document.ticketIds);
|
||||
} catch (e) {
|
||||
throw ('Errore durante il salvataggio e upload della spedizione con allegato: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flux/features/attachments/models/attachment_model.dart';
|
||||
|
||||
class ShipmentDocumentModel extends Equatable {
|
||||
class ShippingDocumentModel extends Equatable {
|
||||
final String? id;
|
||||
final String companyId;
|
||||
final List<String> ticketIds;
|
||||
@@ -12,8 +13,9 @@ class ShipmentDocumentModel extends Equatable {
|
||||
final double? weight;
|
||||
final String shippingReason;
|
||||
final String? notes;
|
||||
final List<AttachmentModel> attachments;
|
||||
|
||||
const ShipmentDocumentModel({
|
||||
const ShippingDocumentModel({
|
||||
this.id,
|
||||
required this.companyId,
|
||||
required this.ticketIds,
|
||||
@@ -25,9 +27,10 @@ class ShipmentDocumentModel extends Equatable {
|
||||
this.weight,
|
||||
this.shippingReason = 'Riparazione esterna',
|
||||
this.notes,
|
||||
this.attachments = const [],
|
||||
});
|
||||
|
||||
ShipmentDocumentModel copyWith({
|
||||
ShippingDocumentModel copyWith({
|
||||
String? id,
|
||||
String? companyId,
|
||||
List<String>? ticketIds,
|
||||
@@ -39,8 +42,9 @@ class ShipmentDocumentModel extends Equatable {
|
||||
double? weight,
|
||||
String? shippingReason,
|
||||
String? notes,
|
||||
List<AttachmentModel>? attachments,
|
||||
}) {
|
||||
return ShipmentDocumentModel(
|
||||
return ShippingDocumentModel(
|
||||
id: id ?? this.id,
|
||||
companyId: companyId ?? this.companyId,
|
||||
ticketIds: ticketIds ?? this.ticketIds,
|
||||
@@ -53,11 +57,12 @@ class ShipmentDocumentModel extends Equatable {
|
||||
weight: weight ?? this.weight,
|
||||
shippingReason: shippingReason ?? this.shippingReason,
|
||||
notes: notes ?? this.notes,
|
||||
attachments: attachments ?? this.attachments,
|
||||
);
|
||||
}
|
||||
|
||||
factory ShipmentDocumentModel.fromMap(Map<String, dynamic> map) {
|
||||
return ShipmentDocumentModel(
|
||||
factory ShippingDocumentModel.fromMap(Map<String, dynamic> map) {
|
||||
return ShippingDocumentModel(
|
||||
id: map['id'],
|
||||
companyId: map['company_id'],
|
||||
ticketIds: List<String>.from(map['ticket_ids']),
|
||||
@@ -69,6 +74,11 @@ class ShipmentDocumentModel extends Equatable {
|
||||
weight: map['weight'] != null ? (map['weight'] as num).toDouble() : null,
|
||||
shippingReason: map['shipping_reason'] ?? 'Riparazione esterna',
|
||||
notes: map['notes'],
|
||||
attachments:
|
||||
(map['attachments'] as List<dynamic>?)
|
||||
?.map((e) => AttachmentModel.fromMap(e))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,5 +99,17 @@ class ShipmentDocumentModel extends Equatable {
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, docNumber, ticketIds];
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
docNumber,
|
||||
ticketIds,
|
||||
providerId,
|
||||
destinationLocationId,
|
||||
docDate,
|
||||
packageCount,
|
||||
weight,
|
||||
shippingReason,
|
||||
notes,
|
||||
attachments,
|
||||
];
|
||||
}
|
||||
@@ -1,7 +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';
|
||||
import 'package:flux/features/tickets/models/shipping_document_model.dart';
|
||||
|
||||
/// Enum per il tipo di ticket
|
||||
enum TicketType {
|
||||
@@ -121,8 +121,7 @@ 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
|
||||
final ShippingDocumentModel? shippingDocument;
|
||||
|
||||
const TicketModel({
|
||||
this.id,
|
||||
@@ -215,7 +214,7 @@ class TicketModel extends Equatable {
|
||||
String? assignedToId,
|
||||
String? assignedToName,
|
||||
String? includedAccessories,
|
||||
ShipmentDocumentModel? shippingDocument,
|
||||
ShippingDocumentModel? shippingDocument,
|
||||
}) {
|
||||
return TicketModel(
|
||||
id: id ?? this.id,
|
||||
@@ -312,7 +311,7 @@ class TicketModel extends Equatable {
|
||||
assignedToName: (map['assigned_to']?['name'] as String?)?.myFormat(),
|
||||
includedAccessories: map['included_accessories'] as String?,
|
||||
shippingDocument: map['shipping_document'] != null
|
||||
? ShipmentDocumentModel.fromMap(
|
||||
? ShippingDocumentModel.fromMap(
|
||||
map['shipping_document'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flux/core/routes/routes.dart';
|
||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||
import 'package:flux/features/attachments/models/attachment_model.dart';
|
||||
import 'package:flux/features/attachments/ui/attachment_viewer_screen.dart';
|
||||
import 'package:flux/features/tickets/blocs/ticket_list_cubit.dart';
|
||||
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||
import 'package:flux/features/tickets/models/ticket_status_extension.dart';
|
||||
@@ -20,6 +23,32 @@ class TicketListCard extends StatelessWidget {
|
||||
required this.isDesktop,
|
||||
});
|
||||
|
||||
void _openFile({
|
||||
required BuildContext context,
|
||||
required AttachmentModel file,
|
||||
}) {
|
||||
// 1. Catturiamo il BLoC dalla pagina corrente prima di navigare
|
||||
final operationFilesBloc = context.read<AttachmentsBloc>();
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (viewerContext) => BlocProvider.value(
|
||||
value: operationFilesBloc,
|
||||
child: AttachmentViewerScreen(
|
||||
attachment: file,
|
||||
onRename: (newName) {
|
||||
// Spara l'evento al BLoC e lui farà il resto!
|
||||
operationFilesBloc.add(RenameAttachmentEvent(file, newName));
|
||||
},
|
||||
onDelete: () {
|
||||
operationFilesBloc.add(DeleteSpecificAttachmentEvent(file));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusColor = ticket.ticketStatus.color;
|
||||
@@ -110,21 +139,57 @@ 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (ticket.shippingDocument != null &&
|
||||
ticket.shippingDocument!.attachments.isNotEmpty) ...[
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.picture_as_pdf,
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
onPressed: () {
|
||||
final attachments =
|
||||
ticket.shippingDocument!.attachments;
|
||||
|
||||
if (attachments.length == 1) {
|
||||
// CASO 1: C'è solo il DDT. Apriamo SUBITO il Document Viewer!
|
||||
_openFile(
|
||||
context: context,
|
||||
file: attachments.first,
|
||||
);
|
||||
} else {
|
||||
// CASO 2: Più allegati. Mostriamo una BottomSheet fulminea per far scegliere all'utente.
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: attachments.length,
|
||||
itemBuilder: (context, index) {
|
||||
final file = attachments[index];
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
file.extension == 'pdf'
|
||||
? Icons.picture_as_pdf
|
||||
: Icons.image,
|
||||
),
|
||||
title: Text(file.name),
|
||||
subtitle: Text(
|
||||
'${(file.fileSize / 1024).toStringAsFixed(1)} KB',
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(
|
||||
context,
|
||||
); // Chiude la scelta
|
||||
_openFile(
|
||||
context: context,
|
||||
file: file,
|
||||
); // Apre il viewer
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:flux/features/company/models/company_model.dart';
|
||||
import 'package:flux/features/tickets/models/shipment_document_model.dart';
|
||||
import 'package:flux/features/tickets/models/shipping_document_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_location_model.dart';
|
||||
import 'package:flux/features/master_data/providers/models/provider_model.dart';
|
||||
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||
@@ -13,7 +13,7 @@ class TicketShippingPdfService {
|
||||
required CompanyModel company,
|
||||
required ProviderModel provider,
|
||||
required ProviderLocationModel location,
|
||||
required ShipmentDocumentModel document,
|
||||
required ShippingDocumentModel document,
|
||||
required List<TicketModel> tickets,
|
||||
}) async {
|
||||
final pdf = pw.Document();
|
||||
|
||||
Reference in New Issue
Block a user