| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- import 'package:flutter/material.dart';
- import 'package:flutter_riverpod/flutter_riverpod.dart';
- import 'package:intl/intl.dart';
- import 'package:ristoris/core/credit_score_color.dart';
- import '../../../providers/auth_provider.dart';
- import '../../widgets/cod_desc_dropdown_item.dart';
- import '../../../providers/order_provider.dart';
- import '../../../providers/database_provider.dart';
- import '../../../database/database.dart';
- import '../../../core/constants.dart';
- import 'selezione_cliente_page.dart';
- import 'selezione_articolo_page.dart';
- class OrderEditPage extends ConsumerStatefulWidget {
- final int? id;
- final int? idCliente;
- const OrderEditPage({super.key, this.id, this.idCliente});
- @override
- ConsumerState<OrderEditPage> createState() => _OrderEditPageState();
- }
- class _OrderEditPageState extends ConsumerState<OrderEditPage>
- with SingleTickerProviderStateMixin {
- late TabController _tabController;
- final _noteClienteCtrl = TextEditingController();
- final _noteOrdineCtrl = TextEditingController();
- final _riferimentoCtrl = TextEditingController();
- List<Filiale> _filiali = [];
- List<Listino> _listini = [];
- List<Pagamento> _pagamenti = [];
- Cliente? _cliente;
- int? _selectedFiliale;
- int? _selectedListino;
- int? _selectedPagamento;
- bool _dataLoaded = false;
- bool _readOnly = false;
- @override
- void initState() {
- super.initState();
- _tabController = TabController(length: 2, vsync: this);
- _tabController.addListener(() => setState(() {}));
- WidgetsBinding.instance.addPostFrameCallback((_) => _init());
- }
- @override
- void dispose() {
- _tabController.dispose();
- _noteClienteCtrl.dispose();
- _noteOrdineCtrl.dispose();
- _riferimentoCtrl.dispose();
- super.dispose();
- }
- Future<void> _init() async {
- final agente = ref.read(authProvider).agente;
- if (agente == null) return;
- if (widget.id != null) {
- await ref.read(orderProvider.notifier).loadOrder(widget.id!);
- } else if (widget.idCliente != null) {
- await ref
- .read(orderProvider.notifier)
- .createOrder(agente.idAgente, idCliente: widget.idCliente!);
- } else {
- await ref.read(orderProvider.notifier).createOrder(agente.idAgente);
- }
- final orderState = ref.read(orderProvider);
- final order = orderState.order;
- if (order == null) return;
- if (widget.id != null && order.stato != AppConstants.kStatusNew) {
- _readOnly = true;
- }
- final cli = order.idCliente;
- final clientiDao = ref.read(clientiDaoProvider);
- final filialiDao = ref.read(filialiDaoProvider);
- final listiniDao = ref.read(listiniDaoProvider);
- final pagamentiDao = ref.read(pagamentiDaoProvider);
- if (cli != null) {
- _cliente = await clientiDao.getById(cli);
- _filiali = await filialiDao.getDestinationsForCustomer(cli);
- }
- _listini = await listiniDao.list();
- _pagamenti = await pagamentiDao.searchActive();
- setState(() {
- _selectedFiliale = order.idFiliale;
- _selectedListino = order.idListino ?? _cliente?.idListino;
- _selectedPagamento = order.idPagamento ?? _cliente?.idPagamento;
- _noteClienteCtrl.text = order.noteCliente ?? _cliente?.noteOrdine ?? '';
- _noteOrdineCtrl.text = order.noteOrdine ?? '';
- _riferimentoCtrl.text = order.riferimento ?? '';
- _dataLoaded = true;
- });
- }
- void _aggiungiArticolo() async {
- final order = ref.read(orderProvider).order;
- final dataOrdine = order?.dataOrdine ?? DateTime.now().toIso8601String();
- final result = await Navigator.push<ArticleSelectionResult>(
- context,
- MaterialPageRoute(
- builder: (_) => SelezioneArticoloPage(
- idCliente: _cliente?.idCliente ?? 0,
- dataOrdine: dataOrdine,
- idListinoDefault: _selectedListino,
- ),
- ),
- );
- if (result != null) {
- _addArticleWithDetails(
- art: result.articolo,
- idListino: result.idListino,
- colli: result.colli,
- quantita: result.quantita,
- prezzoLordo: result.prezzoLordo,
- prezzoNetto: result.prezzoNetto,
- sconto1: result.sconto1,
- sconto2: result.sconto2,
- sconto3: result.sconto3,
- sconto4: result.sconto4,
- sconto5: result.sconto5,
- );
- }
- }
- void _addArticleWithDetails({
- required Articolo art,
- required int idListino,
- required double colli,
- required double quantita,
- required double prezzoLordo,
- required double prezzoNetto,
- double? sconto1 = 0,
- double? sconto2 = 0,
- double? sconto3 = 0,
- double? sconto4 = 0,
- double? sconto5 = 0,
- }) {
- ref.read(orderProvider.notifier).addLine(
- RigaOrdine(
- idRigaOrdine: 0,
- idOrdine: ref.read(orderProvider).order?.idOrdine ?? 0,
- idArticolo: art.idArticolo,
- idListino: idListino,
- quantita: quantita,
- prezzoLordo: prezzoLordo,
- prezzoNetto: prezzoNetto,
- sconto1: sconto1,
- sconto2: sconto2,
- sconto3: sconto3,
- sconto4: sconto4,
- sconto5: sconto5,
- totale: prezzoNetto * quantita,
- promozione: 0,
- rigaPromozione: 0,
- colli: colli,
- ),
- );
- }
- Future<void> _selezionaCliente() async {
- final agente = ref.read(authProvider).agente;
- if (agente == null) return;
- final cliente = await Navigator.push<Cliente>(
- context,
- MaterialPageRoute(
- builder: (_) => SelezioneClientePage(idAgente: agente.idAgente),
- ),
- );
- if (cliente != null) _onClienteSelezionato(cliente);
- }
- void _onClienteSelezionato(Cliente cliente) async {
- ref.read(orderProvider.notifier).setIdCliente(cliente.idCliente);
- final filialiDao = ref.read(filialiDaoProvider);
- final filiali = await filialiDao.getDestinationsForCustomer(
- cliente.idCliente,
- );
- setState(() {
- _cliente = cliente;
- _selectedFiliale = null;
- _selectedPagamento = cliente.idPagamento;
- _selectedListino = cliente.idListino;
- _filiali = filiali;
- _noteClienteCtrl.text = cliente.noteOrdine ?? '';
- });
- }
- Future<void> _salva() async {
- final notifier = ref.read(orderProvider.notifier);
- notifier.setIdFiliale(_selectedFiliale);
- notifier.setIdListino(_selectedListino);
- notifier.setIdPagamento(_selectedPagamento);
- notifier.setNoteCliente(_noteClienteCtrl.text);
- notifier.setNoteOrdine(_noteOrdineCtrl.text);
- notifier.setRiferimento(_riferimentoCtrl.text);
- await notifier.saveOrder();
- if (context.mounted) Navigator.pop(context);
- }
- @override
- Widget build(BuildContext context) {
- final orderState = ref.watch(orderProvider);
- final theme = Theme.of(context);
- return Scaffold(
- appBar: AppBar(
- title: Text(_readOnly
- ? 'Ordine #${orderState.order?.numeroOrdine} - Trasmesso'
- : 'Ordine #${orderState.order?.numeroOrdine ?? 'nuovo'}'),
- actions: [
- if (!_readOnly)
- IconButton(
- icon: const Icon(Icons.save),
- onPressed: _salva,
- ),
- ],
- bottom: TabBar(
- controller: _tabController,
- tabs: const [
- Tab(text: 'Testata'),
- Tab(text: 'Righe'),
- ],
- ),
- ),
- body: SafeArea(
- child: orderState.isLoading || !_dataLoaded
- ? const Center(child: CircularProgressIndicator())
- : TabBarView(
- controller: _tabController,
- children: [
- _buildHeaderTab(theme),
- _buildLinesTab(theme, orderState),
- ],
- ),
- ),
- floatingActionButton:
- _tabController.index == 1 && _cliente != null && !_readOnly
- ? FloatingActionButton(
- onPressed: _aggiungiArticolo,
- child: const Icon(Icons.add),
- )
- : null,
- );
- }
- Widget _buildHeaderTab(ThemeData theme) {
- final cs = theme.colorScheme;
- final bgColor = creditScoreBg(_cliente?.creditScoreColor);
- return SingleChildScrollView(
- padding: const EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Card(
- elevation: 0,
- color: Colors.white,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- side: BorderSide(color: cs.outlineVariant),
- ),
- child: Padding(
- padding: const EdgeInsets.all(12),
- child: Row(
- children: [
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- 'Cliente',
- style: TextStyle(
- fontSize: 12,
- fontWeight: FontWeight.w600,
- color: cs.onSurfaceVariant,
- ),
- ),
- const SizedBox(height: 4),
- if (_cliente != null)
- Row(
- children: [
- Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 8,
- vertical: 4,
- ),
- decoration: BoxDecoration(
- color: bgColor ?? cs.surfaceVariant,
- borderRadius: BorderRadius.circular(4),
- ),
- child: Text(
- _cliente!.codCliente ?? '',
- style: TextStyle(
- fontSize: 12,
- height: 16 / 12,
- fontWeight: FontWeight.w500,
- color: bgColor != null
- ? Colors.white
- : cs.onSurfaceVariant,
- ),
- ),
- ),
- const SizedBox(width: 8),
- Expanded(
- child: Text(
- _cliente!.ragioneSociale ?? '',
- style: theme.textTheme.bodyMedium?.copyWith(
- fontWeight: FontWeight.w600,
- ),
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- ),
- ],
- )
- else
- Text(
- 'Nessun cliente selezionato',
- style: theme.textTheme.bodyMedium?.copyWith(
- color: cs.onSurfaceVariant,
- ),
- ),
- ],
- ),
- ),
- if (!_readOnly)
- IconButton(
- icon: Icon(Icons.search, color: cs.primary),
- onPressed: _selezionaCliente,
- ),
- ],
- ),
- ),
- ),
- const SizedBox(height: 16),
- DropdownButtonFormField<int>(
- value: _selectedFiliale,
- decoration: const InputDecoration(labelText: 'Filiale'),
- items: _filiali
- .map(
- (f) => DropdownMenuItem(
- value: f.idFiliale,
- child: CodDescDropdownItem(
- codice: f.codFiliale,
- descrizione: f.descrizione,
- ),
- ),
- )
- .toList(),
- onChanged:
- _readOnly ? null : (v) => setState(() => _selectedFiliale = v),
- ),
- const SizedBox(height: 16),
- DropdownButtonFormField<int>(
- initialValue: _selectedListino,
- decoration: const InputDecoration(labelText: 'Listino'),
- items: _listini
- .map(
- (l) => DropdownMenuItem(
- value: l.idListino,
- child: CodDescDropdownItem(
- codice: l.codListino,
- descrizione: l.descrizione,
- ),
- ),
- )
- .toList(),
- onChanged:
- _readOnly ? null : (v) => setState(() => _selectedListino = v),
- ),
- const SizedBox(height: 16),
- DropdownButtonFormField<int>(
- initialValue: _selectedPagamento,
- decoration: const InputDecoration(labelText: 'Pagamento'),
- items: _pagamenti
- .map(
- (p) => DropdownMenuItem(
- value: p.idPagamento,
- child: CodDescDropdownItem(
- codice: p.codPagamento,
- descrizione: p.descrizione,
- ),
- ),
- )
- .toList(),
- onChanged: _readOnly
- ? null
- : (v) => setState(() => _selectedPagamento = v),
- ),
- const SizedBox(height: 16),
- TextFormField(
- controller: _noteClienteCtrl,
- enabled: !_readOnly,
- decoration: const InputDecoration(
- labelText: 'Note Cliente',
- alignLabelWithHint: true,
- ),
- maxLines: 3,
- ),
- const SizedBox(height: 16),
- TextFormField(
- controller: _noteOrdineCtrl,
- enabled: !_readOnly,
- decoration: const InputDecoration(
- labelText: 'Note Ordine',
- alignLabelWithHint: true,
- ),
- maxLines: 3,
- ),
- const SizedBox(height: 16),
- TextFormField(
- controller: _riferimentoCtrl,
- enabled: !_readOnly,
- decoration: const InputDecoration(labelText: 'Riferimento'),
- maxLines: 1,
- ),
- ],
- ),
- );
- }
- Widget _buildLinesTab(ThemeData theme, OrderState orderState) {
- return Column(
- children: [
- Expanded(
- child: orderState.lines.isEmpty
- ? const Center(child: Text('Nessun articolo'))
- : ListView.builder(
- itemCount: orderState.lines.length,
- itemBuilder: (_, i) => _LineCard(
- line: orderState.lines[i],
- readOnly: _readOnly,
- onDelete: () =>
- ref.read(orderProvider.notifier).removeLine(i),
- theme: theme,
- ),
- ),
- ),
- Container(
- padding: const EdgeInsets.all(16),
- decoration: BoxDecoration(
- color: theme.colorScheme.surfaceContainerHighest,
- border: Border(top: BorderSide(color: theme.dividerColor)),
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text('Qtà: ${orderState.totQta.toStringAsFixed(1)}'),
- Text(
- 'Totale: ${NumberFormat.currency(locale: 'it', symbol: '€').format(orderState.totale)}',
- style: theme.textTheme.titleMedium?.copyWith(
- fontWeight: FontWeight.bold,
- ),
- ),
- ],
- ),
- ),
- ],
- );
- }
- }
- class _LineCard extends StatelessWidget {
- final RigaOrdine line;
- final bool readOnly;
- final VoidCallback onDelete;
- final ThemeData theme;
- const _LineCard({
- required this.line,
- required this.readOnly,
- required this.onDelete,
- required this.theme,
- });
- @override
- Widget build(BuildContext context) {
- final isPromo = line.rigaPromozione == 1;
- return Card(
- margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
- color: isPromo ? Colors.green.shade50 : null,
- child: ListTile(
- title: Text(
- 'Articolo #${line.idArticolo ?? ''}',
- style: TextStyle(fontWeight: isPromo ? FontWeight.bold : null),
- ),
- subtitle: Text(
- 'Qtà: ${line.quantita?.toStringAsFixed(1) ?? "0"} Prezzo: ${line.prezzoNetto?.toStringAsFixed(2) ?? "0,00"}€',
- ),
- trailing: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (isPromo)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
- decoration: BoxDecoration(
- color: Colors.green,
- borderRadius: BorderRadius.circular(8),
- ),
- child: const Text(
- 'PROMO',
- style: TextStyle(color: Colors.white, fontSize: 10),
- ),
- ),
- if (!isPromo && !readOnly)
- IconButton(
- icon: const Icon(Icons.delete_outline, size: 20),
- onPressed: onDelete,
- ),
- ],
- ),
- ),
- );
- }
- }
|