boh
This commit is contained in:
@@ -190,7 +190,7 @@ class HomeScreen extends StatelessWidget {
|
|||||||
// Entriamo nel form! Nessun parametro extra = Nuovo Servizio
|
// Entriamo nel form! Nessun parametro extra = Nuovo Servizio
|
||||||
context.pushNamed(
|
context.pushNamed(
|
||||||
Routes.operationForm,
|
Routes.operationForm,
|
||||||
pathParameters: {'id': 'New'},
|
pathParameters: {'id': 'new'},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -203,7 +203,7 @@ class HomeScreen extends StatelessWidget {
|
|||||||
// Andiamo alla lista! (Da lì poi aggiungeremo il tasto "+" per il form)
|
// Andiamo alla lista! (Da lì poi aggiungeremo il tasto "+" per il form)
|
||||||
context.pushNamed(
|
context.pushNamed(
|
||||||
Routes.ticketForm,
|
Routes.ticketForm,
|
||||||
pathParameters: {'id': 'New'},
|
pathParameters: {'id': 'new'},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -193,6 +193,6 @@ class _OperationListScreenState extends State<OperationListScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void startNewOperation(BuildContext context) {
|
void startNewOperation(BuildContext context) {
|
||||||
context.pushNamed('operation-form');
|
context.pushNamed('operation-form', pathParameters: {'id': 'new'});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,14 +73,15 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
ticket: state.ticket.copyWith(
|
ticket: state.ticket.copyWith(
|
||||||
customerId: customer.id,
|
customerId: customer.id,
|
||||||
customerName: customer.name,
|
customerName: customer.name,
|
||||||
alternativePhoneNumber: customer.phoneNumber, // Comodo come fallback!
|
alternativePhoneNumber: customer.phoneNumber,
|
||||||
|
customerEmail: customer.email,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 3. AGGIORNAMENTO MODELLO (Usato dal nostro SharedModelSection!)
|
/// 3. AGGIORNAMENTO MODELLO (Usato dal nostro SharedModelSection!)
|
||||||
void updateModel({required String modelId, required String modelName}) {
|
void updateTargetModel({required String modelId, required String modelName}) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
ticket: state.ticket.copyWith(
|
ticket: state.ticket.copyWith(
|
||||||
@@ -143,7 +144,7 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 5. SALVATAGGIO
|
/// 5. SALVATAGGIO
|
||||||
Future<void> saveTicket({required bool keepAdding}) async {
|
Future<void> saveTicket() async {
|
||||||
emit(state.copyWith(status: TicketFormStatus.saving));
|
emit(state.copyWith(status: TicketFormStatus.saving));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -159,28 +160,15 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
} else {
|
} else {
|
||||||
savedTicket = await _repository.updateTicket(ticketToSave);
|
savedTicket = await _repository.updateTicket(ticketToSave);
|
||||||
}
|
}
|
||||||
|
emit(
|
||||||
if (keepAdding) {
|
state.copyWith(
|
||||||
emit(
|
status: TicketFormStatus.success,
|
||||||
state.copyWith(
|
ticket: ticketToSave.copyWith(
|
||||||
status: TicketFormStatus.successAndAddAnother,
|
id: savedTicket.id,
|
||||||
// Svuotiamo il form per il prossimo, mantenendo Store e Creatore ATTUALI
|
referenceId: savedTicket.referenceId,
|
||||||
ticket: TicketModel.empty().copyWith(
|
|
||||||
companyId: savedTicket.companyId,
|
|
||||||
storeId: savedTicket.storeId,
|
|
||||||
createdById: ticketToSave
|
|
||||||
.createdById, // Manteniamo quello selezionato nella tendina!
|
|
||||||
createdByName: ticketToSave.createdByName,
|
|
||||||
ticketStatus: TicketStatus.open,
|
|
||||||
ticketType: TicketType.repair,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
} else {
|
);
|
||||||
emit(
|
|
||||||
state.copyWith(status: TicketFormStatus.success, ticket: savedTicket),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
|
|||||||
@@ -2,15 +2,7 @@ import 'package:equatable/equatable.dart';
|
|||||||
import 'package:flux/features/tickets/models/ticket_model.dart';
|
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||||
// Adatta gli import al tuo progetto!
|
// Adatta gli import al tuo progetto!
|
||||||
|
|
||||||
enum TicketFormStatus {
|
enum TicketFormStatus { initial, ready, loading, saving, success, pop, failure }
|
||||||
initial,
|
|
||||||
ready,
|
|
||||||
loading,
|
|
||||||
saving,
|
|
||||||
success,
|
|
||||||
successAndAddAnother,
|
|
||||||
failure,
|
|
||||||
}
|
|
||||||
|
|
||||||
class TicketFormState extends Equatable {
|
class TicketFormState extends Equatable {
|
||||||
final TicketModel ticket;
|
final TicketModel ticket;
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ class TicketModel extends Equatable {
|
|||||||
final TicketResult? ticketResult;
|
final TicketResult? ticketResult;
|
||||||
final String? resolutionNotes;
|
final String? resolutionNotes;
|
||||||
final String? customerName;
|
final String? customerName;
|
||||||
|
final String? customerEmail;
|
||||||
final String? targetModelName;
|
final String? targetModelName;
|
||||||
final String? sourceModelName;
|
final String? sourceModelName;
|
||||||
final String? createdById;
|
final String? createdById;
|
||||||
@@ -142,6 +143,7 @@ class TicketModel extends Equatable {
|
|||||||
this.ticketResult,
|
this.ticketResult,
|
||||||
this.resolutionNotes,
|
this.resolutionNotes,
|
||||||
this.customerName,
|
this.customerName,
|
||||||
|
this.customerEmail,
|
||||||
this.targetModelName,
|
this.targetModelName,
|
||||||
this.sourceModelName,
|
this.sourceModelName,
|
||||||
this.createdById,
|
this.createdById,
|
||||||
@@ -192,6 +194,7 @@ class TicketModel extends Equatable {
|
|||||||
TicketResult? ticketResult,
|
TicketResult? ticketResult,
|
||||||
String? resolutionNotes,
|
String? resolutionNotes,
|
||||||
String? customerName,
|
String? customerName,
|
||||||
|
String? customerEmail,
|
||||||
String? targetModelName,
|
String? targetModelName,
|
||||||
String? sourceModelName,
|
String? sourceModelName,
|
||||||
String? createdById,
|
String? createdById,
|
||||||
@@ -228,6 +231,7 @@ class TicketModel extends Equatable {
|
|||||||
ticketResult: ticketResult ?? this.ticketResult,
|
ticketResult: ticketResult ?? this.ticketResult,
|
||||||
resolutionNotes: resolutionNotes ?? this.resolutionNotes,
|
resolutionNotes: resolutionNotes ?? this.resolutionNotes,
|
||||||
customerName: customerName ?? this.customerName,
|
customerName: customerName ?? this.customerName,
|
||||||
|
customerEmail: customerEmail ?? this.customerEmail,
|
||||||
targetModelName: targetModelName ?? this.targetModelName,
|
targetModelName: targetModelName ?? this.targetModelName,
|
||||||
sourceModelName: sourceModelName ?? this.sourceModelName,
|
sourceModelName: sourceModelName ?? this.sourceModelName,
|
||||||
createdById: createdById ?? this.createdById,
|
createdById: createdById ?? this.createdById,
|
||||||
@@ -276,6 +280,7 @@ class TicketModel extends Equatable {
|
|||||||
ticketResult: TicketResult.fromString(map['ticket_result'] as String?),
|
ticketResult: TicketResult.fromString(map['ticket_result'] as String?),
|
||||||
resolutionNotes: map['resolution_notes'] as String?,
|
resolutionNotes: map['resolution_notes'] as String?,
|
||||||
customerName: (map['customer']?['name'] as String?).myFormat(),
|
customerName: (map['customer']?['name'] as String?).myFormat(),
|
||||||
|
customerEmail: (map['customer']?['email'] as String?).myFormat(),
|
||||||
targetModelName: (map['target_model']?['name_with_brand'] as String?)
|
targetModelName: (map['target_model']?['name_with_brand'] as String?)
|
||||||
?.myFormat(),
|
?.myFormat(),
|
||||||
sourceModelName: (map['source_model']?['name_with_brand'] as String?)
|
sourceModelName: (map['source_model']?['name_with_brand'] as String?)
|
||||||
@@ -350,6 +355,7 @@ class TicketModel extends Equatable {
|
|||||||
resolutionNotes,
|
resolutionNotes,
|
||||||
includedAccessories,
|
includedAccessories,
|
||||||
customerName,
|
customerName,
|
||||||
|
customerEmail,
|
||||||
targetModelName,
|
targetModelName,
|
||||||
sourceModelName,
|
sourceModelName,
|
||||||
createdById,
|
createdById,
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/customer_section.dart';
|
import 'package:flux/core/widgets/shared_forms/customer_section.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/model_section.dart';
|
import 'package:flux/core/widgets/shared_forms/model_section.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_files_section.dart';
|
import 'package:flux/core/widgets/shared_forms/shared_files_section.dart';
|
||||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||||
|
import 'package:flux/features/company/models/company_model.dart';
|
||||||
import 'package:flux/features/tickets/blocs/ticket_form_cubit.dart';
|
import 'package:flux/features/tickets/blocs/ticket_form_cubit.dart';
|
||||||
import 'package:flux/features/tickets/blocs/ticket_form_state.dart';
|
import 'package:flux/features/tickets/blocs/ticket_form_state.dart';
|
||||||
import 'package:flux/features/tickets/models/ticket_model.dart';
|
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/staff_section.dart';
|
import 'package:flux/core/widgets/shared_forms/staff_section.dart';
|
||||||
import 'package:flux/features/tickets/models/ticket_status_extension.dart';
|
import 'package:flux/features/tickets/models/ticket_status_extension.dart';
|
||||||
|
import 'package:flux/features/tickets/utils/ticket_pdf_service.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:pdf/pdf.dart';
|
||||||
|
import 'package:printing/printing.dart';
|
||||||
|
|
||||||
class TicketFormScreen extends StatefulWidget {
|
class TicketFormScreen extends StatefulWidget {
|
||||||
final TicketModel? existingTicket;
|
final TicketModel? existingTicket;
|
||||||
@@ -93,10 +99,10 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _saveTicket({required bool keepAdding}) {
|
void _saveTicket() {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
_flushControllersToCubit();
|
_flushControllersToCubit();
|
||||||
context.read<TicketFormCubit>().saveTicket(keepAdding: keepAdding);
|
context.read<TicketFormCubit>().saveTicket();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +127,110 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
return newId;
|
return newId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showSuccessActions(
|
||||||
|
BuildContext context,
|
||||||
|
TicketModel ticket,
|
||||||
|
CompanyModel company,
|
||||||
|
) {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isDismissible: false, // Costringiamo l'operatore a una scelta conscia
|
||||||
|
isScrollControlled: true,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||||
|
),
|
||||||
|
builder: (context) => SafeArea(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
const Icon(Icons.check_circle, color: Colors.green, size: 64),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
"Ticket Salvato!",
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Rif: ${ticket.referenceId}",
|
||||||
|
style: TextStyle(color: Colors.grey[600], fontSize: 18),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
|
// Griglia delle Azioni
|
||||||
|
GridView.count(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
shrinkWrap: true,
|
||||||
|
mainAxisSpacing: 16,
|
||||||
|
crossAxisSpacing: 16,
|
||||||
|
childAspectRatio: 1.5,
|
||||||
|
children: [
|
||||||
|
_ActionButton(
|
||||||
|
icon: Icons.print,
|
||||||
|
label: "Ricevuta A4",
|
||||||
|
onTap: () async {
|
||||||
|
// 1. Costruiamo la struttura (velocissimo)
|
||||||
|
final doc = await TicketPdfService()
|
||||||
|
.generateTicketReceipt(ticket, company);
|
||||||
|
|
||||||
|
// 2. Lanciamo layoutPdf esattamente come facevi tu!
|
||||||
|
await Printing.layoutPdf(
|
||||||
|
name: 'Ricevuta_${ticket.referenceId}.pdf',
|
||||||
|
onLayout: (PdfPageFormat format) async =>
|
||||||
|
doc.save(), // La magia è qui!
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (company.labelFormat != LabelFormat.none)
|
||||||
|
_ActionButton(
|
||||||
|
icon: Icons.label,
|
||||||
|
label: "Etichetta",
|
||||||
|
onTap: () async {
|
||||||
|
final doc = await TicketPdfService().generateLabelPdf(
|
||||||
|
ticket,
|
||||||
|
company,
|
||||||
|
);
|
||||||
|
await Printing.layoutPdf(
|
||||||
|
name: 'Etichetta_${ticket.referenceId}.pdf',
|
||||||
|
onLayout: (PdfPageFormat format) async => doc.save(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_ActionButton(
|
||||||
|
icon: Icons.email,
|
||||||
|
label: "Invia Email",
|
||||||
|
onTap: ticket.customerEmail != null ? () {} : null,
|
||||||
|
),
|
||||||
|
_ActionButton(
|
||||||
|
icon: Icons.close,
|
||||||
|
label: "Chiudi",
|
||||||
|
color: Colors.blue[900],
|
||||||
|
textColor: Colors.white,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
@@ -133,22 +243,13 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.status == TicketFormStatus.success) {
|
if (state.status == TicketFormStatus.success) {
|
||||||
Navigator.of(context).pop();
|
_showSuccessActions(
|
||||||
} else if (state.status == TicketFormStatus.successAndAddAnother) {
|
context,
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
state.ticket,
|
||||||
const SnackBar(
|
GetIt.I.get<SessionCubit>().state.company!,
|
||||||
content: Text('Scheda salvata! Inserisci la prossima.'),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
_altPhoneCtrl.clear();
|
} else if (state.status == TicketFormStatus.pop) {
|
||||||
_serialCtrl.clear();
|
Navigator.of(context).pop();
|
||||||
_requestCtrl.clear();
|
|
||||||
_accessoriesCtrl.clear();
|
|
||||||
_publicNotesCtrl.clear();
|
|
||||||
_internalNotesCtrl.clear();
|
|
||||||
_priceCtrl.clear();
|
|
||||||
_costCtrl.clear();
|
|
||||||
_isInitialized = false;
|
|
||||||
} else if (state.status == TicketFormStatus.failure) {
|
} else if (state.status == TicketFormStatus.failure) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
@@ -229,23 +330,23 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: OutlinedButton(
|
child: ElevatedButton(
|
||||||
onPressed: state.status == TicketFormStatus.saving
|
onPressed: state.ticket.id == null
|
||||||
? null
|
? null
|
||||||
: () => _saveTicket(keepAdding: true),
|
: () => _showSuccessActions(
|
||||||
child: const Text(
|
context,
|
||||||
'Salva e Aggiungi Altro',
|
ticket,
|
||||||
textAlign: TextAlign.center,
|
GetIt.I.get<SessionCubit>().state.company!,
|
||||||
),
|
),
|
||||||
|
child: const Text('Ricevuta'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: state.status == TicketFormStatus.saving
|
onPressed: state.status == TicketFormStatus.saving
|
||||||
? null
|
? null
|
||||||
: () => _saveTicket(keepAdding: false),
|
: () => _saveTicket(),
|
||||||
child: state.status == TicketFormStatus.saving
|
child: state.status == TicketFormStatus.saving
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
@@ -378,7 +479,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
modelName: ticket.targetModelName,
|
modelName: ticket.targetModelName,
|
||||||
onModelSelected: (id, name) => context
|
onModelSelected: (id, name) => context
|
||||||
.read<TicketFormCubit>()
|
.read<TicketFormCubit>()
|
||||||
.updateModel(modelId: id, modelName: name),
|
.updateTargetModel(modelId: id, modelName: name),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
@@ -597,3 +698,55 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Widget helper per i bottoni dell'Action Hub
|
||||||
|
class _ActionButton extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final String label;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final Color? color;
|
||||||
|
final Color? textColor;
|
||||||
|
|
||||||
|
const _ActionButton({
|
||||||
|
required this.icon,
|
||||||
|
required this.label,
|
||||||
|
this.onTap,
|
||||||
|
this.color,
|
||||||
|
this.textColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: onTap == null ? Colors.grey[100] : (color ?? Colors.grey[200]),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
color: onTap == null
|
||||||
|
? Colors.grey
|
||||||
|
: (textColor ?? Colors.blue[900]),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: onTap == null
|
||||||
|
? Colors.grey
|
||||||
|
: (textColor ?? Colors.blue[900]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ class _TicketListScreenState extends State<TicketListScreen> {
|
|||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(Routes.ticketForm, pathParameters: {'id': 'New'});
|
context.pushNamed(Routes.ticketForm, pathParameters: {'id': 'new'});
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
label: const Text('Nuovo Ticket'),
|
label: const Text('Nuovo Ticket'),
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
import 'package:pdf/pdf.dart';
|
import 'package:pdf/pdf.dart';
|
||||||
import 'package:pdf/widgets.dart' as pw;
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
import 'package:printing/printing.dart';
|
import 'package:printing/printing.dart';
|
||||||
@@ -7,7 +6,7 @@ import 'package:flux/features/company/models/company_model.dart';
|
|||||||
|
|
||||||
class TicketPdfService {
|
class TicketPdfService {
|
||||||
/// Funzione principale: Genera il PDF A4 con le due metà
|
/// Funzione principale: Genera il PDF A4 con le due metà
|
||||||
Future<Uint8List> generateTicketReceipt(
|
Future<pw.Document> generateTicketReceipt(
|
||||||
TicketModel ticket,
|
TicketModel ticket,
|
||||||
CompanyModel company,
|
CompanyModel company,
|
||||||
) async {
|
) async {
|
||||||
@@ -16,6 +15,8 @@ class TicketPdfService {
|
|||||||
// Carichiamo il font per essere sicuri che i caratteri siano ok
|
// Carichiamo il font per essere sicuri che i caratteri siano ok
|
||||||
final font = await PdfGoogleFonts.robotoRegular();
|
final font = await PdfGoogleFonts.robotoRegular();
|
||||||
final boldFont = await PdfGoogleFonts.robotoBold();
|
final boldFont = await PdfGoogleFonts.robotoBold();
|
||||||
|
/* final font = pw.Font.helvetica();
|
||||||
|
final boldFont = pw.Font.helveticaBold(); */
|
||||||
|
|
||||||
pdf.addPage(
|
pdf.addPage(
|
||||||
pw.Page(
|
pw.Page(
|
||||||
@@ -60,7 +61,7 @@ class TicketPdfService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return pdf.save();
|
return pdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper per costruire una singola metà (Cliente o Negozio)
|
/// Helper per costruire una singola metà (Cliente o Negozio)
|
||||||
@@ -71,8 +72,8 @@ class TicketPdfService {
|
|||||||
pw.Font boldFont, {
|
pw.Font boldFont, {
|
||||||
required bool isForCustomer,
|
required bool isForCustomer,
|
||||||
}) {
|
}) {
|
||||||
return pw.Container(
|
return pw.Expanded(
|
||||||
height: 380, // Circa metà A4 meno i margini
|
//height: 380, // Circa metà A4 meno i margini
|
||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -276,7 +277,7 @@ class TicketPdfService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List> generateLabelPdf(
|
Future<pw.Document> generateLabelPdf(
|
||||||
TicketModel ticket,
|
TicketModel ticket,
|
||||||
CompanyModel company,
|
CompanyModel company,
|
||||||
) async {
|
) async {
|
||||||
@@ -337,6 +338,6 @@ class TicketPdfService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return pdf.save();
|
return pdf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ PODS:
|
|||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
- pdfx (1.0.0):
|
- pdfx (1.0.0):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- printing (1.0.0):
|
||||||
|
- FlutterMacOS
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@@ -20,6 +22,7 @@ DEPENDENCIES:
|
|||||||
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
|
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- pdfx (from `Flutter/ephemeral/.symlinks/plugins/pdfx/macos`)
|
- pdfx (from `Flutter/ephemeral/.symlinks/plugins/pdfx/macos`)
|
||||||
|
- printing (from `Flutter/ephemeral/.symlinks/plugins/printing/macos`)
|
||||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
|
|
||||||
@@ -34,6 +37,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
pdfx:
|
pdfx:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/pdfx/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/pdfx/macos
|
||||||
|
printing:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/printing/macos
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
@@ -45,6 +50,7 @@ SPEC CHECKSUMS:
|
|||||||
file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7
|
file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7
|
||||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||||
pdfx: 1e79f57f7a6ce2f4a4c30f21fa54d3dc82441b51
|
pdfx: 1e79f57f7a6ce2f4a4c30f21fa54d3dc82441b51
|
||||||
|
printing: c4cf83c78fd684f9bc318e6aadc18972aa48f617
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
|
url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
|
||||||
|
|
||||||
|
|||||||
@@ -23,5 +23,7 @@
|
|||||||
|
|
||||||
<key>com.apple.security.device.audio-input</key>
|
<key>com.apple.security.device.audio-input</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.print</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
<key>com.apple.security.files.user-selected.read-write</key>
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.print</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
Reference in New Issue
Block a user