ticket labels e ticket receipt

This commit is contained in:
2026-05-10 14:09:57 +02:00
parent 385c3da0a5
commit 5c86483563
20 changed files with 1024 additions and 157 deletions

View File

@@ -37,6 +37,11 @@ class CompanySettingsCubit extends Cubit<CompanySettingsState> {
String? zipCode,
String? phone,
String? email,
String? ticketDisclaimer,
LabelFormat? labelFormat,
double? labelWidth,
double? labelHeight,
bool? isVertical,
}) {
if (state.company == null) return;
@@ -51,6 +56,11 @@ class CompanySettingsCubit extends Cubit<CompanySettingsState> {
zipCode: zipCode ?? state.company!.zipCode,
phone: phone ?? state.company!.phone,
email: email ?? state.company!.email,
ticketDisclaimer: ticketDisclaimer ?? state.company!.ticketDisclaimer,
labelFormat: labelFormat ?? state.company!.labelFormat,
labelWidth: labelWidth ?? state.company!.labelWidth,
labelHeight: labelHeight ?? state.company!.labelHeight,
isLabelVertical: isVertical ?? state.company!.isLabelVertical,
);
emit(state.copyWith(company: updated));
}

View File

@@ -35,6 +35,21 @@ enum SubscriptionStatus {
}
}
enum LabelFormat {
none,
small_62x29,
medium_54x101,
large_102x152,
custom;
static LabelFormat fromString(String? value) {
return LabelFormat.values.firstWhere(
(e) => e.name == value,
orElse: () => LabelFormat.none,
);
}
}
// ===================================================================
// IL MODELLO ESATTO
// ===================================================================
@@ -56,7 +71,11 @@ class CompanyModel extends Equatable {
final String? phone;
final String? email;
final String? logoUrl;
final String? ticketDisclaimer;
final LabelFormat labelFormat;
final double? labelWidth;
final double? labelHeight;
final bool isLabelVertical;
// Stato Pagamenti (Ibride: manuale + Stripe)
final bool isPaid;
final DateTime? paymentExpiration;
@@ -83,6 +102,11 @@ class CompanyModel extends Equatable {
this.phone,
this.email,
this.logoUrl,
this.ticketDisclaimer,
this.labelFormat = LabelFormat.none,
this.labelWidth,
this.labelHeight,
this.isLabelVertical = false,
this.isPaid = false,
this.paymentExpiration,
this.subscriptionTier = SubscriptionTier.free,
@@ -105,6 +129,11 @@ class CompanyModel extends Equatable {
String? fiscalCode,
String? sdi,
String? logoUrl,
String? ticketDisclaimer,
LabelFormat? labelFormat,
double? labelWidth,
double? labelHeight,
bool? isLabelVertical,
String? phone,
String? email,
bool? isPaid,
@@ -130,6 +159,11 @@ class CompanyModel extends Equatable {
logoUrl: logoUrl ?? this.logoUrl,
phone: phone ?? this.phone,
email: email ?? this.email,
ticketDisclaimer: ticketDisclaimer ?? this.ticketDisclaimer,
labelFormat: labelFormat ?? this.labelFormat,
labelWidth: labelWidth ?? this.labelWidth,
labelHeight: labelHeight ?? this.labelHeight,
isLabelVertical: isLabelVertical ?? this.isLabelVertical,
isPaid: isPaid ?? this.isPaid,
paymentExpiration: paymentExpiration ?? this.paymentExpiration,
subscriptionTier: subscriptionTier ?? this.subscriptionTier,
@@ -171,9 +205,18 @@ class CompanyModel extends Equatable {
vatId: map['vat_id'] ?? '',
fiscalCode: map['fiscal_code'] ?? '',
sdi: map['sdi'] ?? '',
logoUrl: map['company_logo'],
logoUrl: map['logo_url'],
phone: map['phone'] ?? '',
email: map['email'] ?? '',
ticketDisclaimer: map['ticket_disclaimer'],
labelFormat: LabelFormat.fromString(map['label_format']),
labelWidth: map['label_width'] != null
? (map['label_width'] as num).toDouble()
: null,
labelHeight: map['label_height'] != null
? (map['label_height'] as num).toDouble()
: null,
isLabelVertical: map['is_label_vertical'] ?? false,
isPaid: map['is_paid'] ?? false,
paymentExpiration: map['payment_expiration'] != null
? DateTime.tryParse(map['payment_expiration'])
@@ -203,9 +246,14 @@ class CompanyModel extends Equatable {
'vat_id': vatId,
'fiscal_code': fiscalCode,
'sdi': sdi,
'company_logo': logoUrl,
'logo_url': logoUrl,
'phone': phone,
'email': email,
'ticket_disclaimer': ticketDisclaimer,
'label_format': labelFormat.name,
'label_width': labelWidth,
'label_height': labelHeight,
'is_label_vertical': isLabelVertical,
'is_paid': isPaid,
if (paymentExpiration != null)
'payment_expiration': paymentExpiration!.toIso8601String(),
@@ -236,6 +284,11 @@ class CompanyModel extends Equatable {
logoUrl,
phone,
email,
ticketDisclaimer,
labelFormat,
labelWidth,
labelHeight,
isLabelVertical,
isPaid,
paymentExpiration,
subscriptionTier,

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/features/company/bloc/company_settings_cubit.dart';
import 'package:flux/features/company/models/company_model.dart';
import 'package:flux/features/settings/document_sequence/blocs/document_sequence_cubit.dart';
import 'package:flux/features/settings/document_sequence/ui/document_sequence_section.dart';
import 'package:image_picker/image_picker.dart';
class CompanySettingsScreen extends StatefulWidget {
@@ -24,6 +26,7 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
final _zipCtrl = TextEditingController();
final _phoneCtrl = TextEditingController();
final _emailCtrl = TextEditingController();
final _disclaimerCtrl = TextEditingController();
bool _isInitialized = false;
@@ -50,6 +53,8 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
_zipCtrl.dispose();
_phoneCtrl.dispose();
_emailCtrl.dispose();
_disclaimerCtrl.dispose();
super.dispose();
}
@@ -69,6 +74,9 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
if (_phoneCtrl.text.isEmpty) _phoneCtrl.text = company.phone ?? '';
if (_emailCtrl.text.isEmpty) _emailCtrl.text = company.email ?? '';
_isInitialized = true;
if (_disclaimerCtrl.text.isEmpty) {
_disclaimerCtrl.text = company.ticketDisclaimer ?? '';
}
}
void _flushToCubit() {
@@ -83,6 +91,7 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
zipCode: _zipCtrl.text,
phone: _phoneCtrl.text,
email: _emailCtrl.text,
ticketDisclaimer: _disclaimerCtrl.text,
);
}
@@ -99,6 +108,36 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
}
}
void _onLabelFormatChanged(LabelFormat selectedFormat) {
double? w;
double? h;
switch (selectedFormat) {
case LabelFormat.small_62x29:
w = 62.0;
h = 29.0;
break;
case LabelFormat.medium_54x101:
w = 54.0;
h = 101.0;
break;
case LabelFormat.large_102x152:
w = 102.0;
h = 152.0;
break;
case LabelFormat.custom:
case LabelFormat.none:
// Lasciamo i valori null o quelli vecchi
break;
}
context.read<CompanySettingsCubit>().updateFields(
labelFormat: selectedFormat,
labelWidth: w,
labelHeight: h,
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@@ -312,6 +351,63 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
),
],
),
const SizedBox(height: 16),
BlocProvider(
create: (context) =>
DocumentSequenceCubit(state.company!.id!)
..loadSequences(),
child: const DocumentSequenceSection(),
),
const SizedBox(height: 16),
// Sezione Disclaimer
Text(
"Note Legali Ricevuta",
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 8),
TextFormField(
controller: _disclaimerCtrl,
maxLines: 5,
decoration: const InputDecoration(
hintText:
"Inserisci qui la liberatoria legale che apparirà sulla ricevuta dei ticket...",
border: OutlineInputBorder(),
),
onChanged: (val) => context
.read<CompanySettingsCubit>()
.updateFields(ticketDisclaimer: val),
),
const SizedBox(height: 24),
// Sezione Etichette
Text(
"Configurazione Etichette",
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 8),
DropdownButtonFormField<LabelFormat>(
initialValue: company.labelFormat,
decoration: const InputDecoration(
prefixIcon: Icon(Icons.label_outline),
labelText: "Formato Stampa Etichetta",
),
items: LabelFormat.values
.map(
(f) => DropdownMenuItem(
value: f,
child: Text(
f.name.replaceAll('_', ' ').toUpperCase(),
),
),
)
.toList(),
onChanged: (val) {
if (val != null) {
_onLabelFormatChanged(val);
}
},
),
const SizedBox(height: 48),
// --- PULSANTE SALVATAGGIO ---