import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:ristoris/database/daos/articoli_dao.dart'; import '../../../database/daos/sconti_dao.dart'; import '../../../providers/auth_provider.dart'; import '../../widgets/cod_desc_dropdown_item.dart'; import '../../../providers/database_provider.dart'; import '../../../database/database.dart'; import '../../widgets/article_image.dart'; enum ArticleFilterMode { tutti, offertaCliente, acquistatiInPassato } class ArticleSelectionResult { final Articolo articolo; final int idListino; final double colli; final double quantita; final double prezzoLordo; final double prezzoNetto; final double sconto1; final double sconto2; final double sconto3; final double sconto4; final double sconto5; const ArticleSelectionResult({ required this.articolo, required this.idListino, required this.colli, required this.quantita, required this.prezzoLordo, required this.prezzoNetto, this.sconto1 = 0, this.sconto2 = 0, this.sconto3 = 0, this.sconto4 = 0, this.sconto5 = 0, }); } class SelezioneArticoloPage extends ConsumerStatefulWidget { final int idCliente; final String dataOrdine; final int? idListinoDefault; const SelezioneArticoloPage({ super.key, required this.idCliente, required this.dataOrdine, this.idListinoDefault, }); @override ConsumerState createState() => _SelezioneArticoloPageState(); } class _SelezioneArticoloPageState extends ConsumerState { final _artCtrl = TextEditingController(); List _searchResults = []; bool _searching = false; ArticleFilterMode _filterMode = ArticleFilterMode.offertaCliente; @override void initState() { super.initState(); _searchArticles(''); } @override void dispose() { _artCtrl.dispose(); super.dispose(); } Future _searchArticles(String q) async { setState(() => _searching = true); final articoliDao = ref.read(articoliDaoProvider); final clientiDao = ref.read(clientiDaoProvider); List results; switch (_filterMode) { case ArticleFilterMode.tutti: results = await articoliDao.getActiveItemsWithPromo(q, widget.idCliente); case ArticleFilterMode.offertaCliente: results = await clientiDao.getFavoriteItems( widget.idCliente, widget.dataOrdine, q, ); case ArticleFilterMode.acquistatiInPassato: final dataOrdineParsed = DateTime.parse(widget.dataOrdine); final dataDa180 = dataOrdineParsed.subtract(const Duration(days: 180)); final dataDa180Str = DateFormat('yyyy-MM-dd').format(dataDa180); results = await clientiDao.getAlreadySoldItems( 0, widget.idCliente, dataDa180Str, q, ); } setState(() { _searchResults = results; _searching = false; }); } void _showArticlePicker(int idArticolo) async { final art = await ref.read(articoliDaoProvider).getById(idArticolo); if (art == null || !context.mounted) return; final agente = ref.read(authProvider).agente; final canEditPrice = agente?.modificaPrezzi == 1; final articoliDao = ref.read(articoliDaoProvider); final listini = await articoliDao.getListiniArticoloValidi( art.idArticolo, widget.idCliente, widget.dataOrdine); final listiniCliente = await articoliDao.getListiniClienteValidi( widget.idCliente, art.idArticolo, widget.dataOrdine); final listinoCliente = listiniCliente.isNotEmpty ? listiniCliente.first : null; final scontiDao = ref.read(scontiDaoProvider); if (!context.mounted) return; final sheetResult = await showModalBottomSheet( context: context, isScrollControlled: true, builder: (_) => _ArticlePickerSheet( articolo: art, listini: listini, listinoCliente: listinoCliente, articoliDao: articoliDao, scontiDao: scontiDao, dataOrdine: widget.dataOrdine, canEditPrezzo: canEditPrice, idListinoDefault: widget.idListinoDefault, onConfirm: ( colli, quantita, prezzoLordo, prezzoNetto, listinoId, { double sconto1 = 0, double sconto2 = 0, double sconto3 = 0, double sconto4 = 0, double sconto5 = 0, }) { Navigator.pop( context, ArticleSelectionResult( articolo: art, idListino: listinoId, colli: colli, quantita: quantita, prezzoLordo: prezzoLordo, prezzoNetto: prezzoNetto, sconto1: sconto1, sconto2: sconto2, sconto3: sconto3, sconto4: sconto4, sconto5: sconto5, ), ); }, ), ); if (sheetResult != null && context.mounted) { Navigator.pop(context, sheetResult); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final currencyFormat = NumberFormat.currency(locale: 'it', symbol: '€'); final dateFormat = DateFormat('dd/MM/yyyy'); return Scaffold( appBar: AppBar( title: const Text('Selezione articolo'), ), body: SafeArea( child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), child: TextField( controller: _artCtrl, decoration: InputDecoration( hintText: 'Cerca articolo...', prefixIcon: const Icon(Icons.search), suffixIcon: _searching ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : null, ), onChanged: _searchArticles, ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: SegmentedButton( segments: const [ ButtonSegment( value: ArticleFilterMode.offertaCliente, label: Text('Offerta cliente'), ), ButtonSegment( value: ArticleFilterMode.tutti, label: Text('Tutti'), ), ButtonSegment( value: ArticleFilterMode.acquistatiInPassato, label: Text('Acquistati'), ), ], selected: {_filterMode}, onSelectionChanged: (selected) { setState(() => _filterMode = selected.first); _searchArticles(_artCtrl.text); }, ), ), Expanded( child: _searchResults.isEmpty ? Center( child: Text( _searching ? 'Ricerca...' : 'Nessun articolo trovato', ), ) : ListView.builder( itemCount: _searchResults.length, itemBuilder: (_, i) { final item = _searchResults[i]; final hasLastPurchase = item.ultimoPrezzo != null && item.lastDateSold != null; return Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: ArticleImage( idArticolo: item.idArticolo, codArticolo: item.codArticolo, ), title: Text( '${item.codArticolo} ${item.descrizione}'), // subtitle: Text(item.codArticolo ?? ''), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (item.inPromo == 1) const Icon( Icons.star, color: Colors.amber, size: 20, ), TextButton( onPressed: () => _showArticlePicker(item.idArticolo), child: const Text('Aggiungi'), ), ], ), ), if (hasLastPurchase) Padding( padding: const EdgeInsets.only( left: 72, right: 16, bottom: 8, ), child: Row( children: [ Text( 'Ultimo acquisto: ', style: theme.textTheme.bodySmall, ), Text( currencyFormat.format(item.ultimoPrezzo), style: theme.textTheme.bodySmall ?.copyWith( fontWeight: FontWeight.bold), ), const SizedBox(width: 8), Text( dateFormat.format( DateTime.parse(item.lastDateSold!), ), style: theme.textTheme.bodySmall, ), ], ), ), ], ); }, ), ), ], ), ), ); } } const int _kListinoClienteSentinel = -1; class _ArticlePickerSheet extends StatefulWidget { final Articolo articolo; final List listini; final ListinoCliente? listinoCliente; final ArticoliDao articoliDao; final ScontiDao scontiDao; final String dataOrdine; final bool canEditPrezzo; final int? idListinoDefault; final void Function( double colli, double quantita, double prezzoLordo, double prezzoNetto, int listinoId, { double sconto1, double sconto2, double sconto3, double sconto4, double sconto5, }) onConfirm; const _ArticlePickerSheet({ required this.articolo, required this.listini, this.listinoCliente, required this.articoliDao, required this.scontiDao, required this.dataOrdine, required this.canEditPrezzo, this.idListinoDefault, required this.onConfirm, }); @override State<_ArticlePickerSheet> createState() => _ArticlePickerSheetState(); } class _ArticlePickerSheetState extends State<_ArticlePickerSheet> { final _colliCtrl = TextEditingController(); final _qtaCtrl = TextEditingController(); final _prezzoCtrl = TextEditingController(); final _sconto1Ctrl = TextEditingController(); final _sconto2Ctrl = TextEditingController(); final _sconto3Ctrl = TextEditingController(); final _sconto4Ctrl = TextEditingController(); final _sconto5Ctrl = TextEditingController(); int? _selectedListino; double _prezzo = 0; double _prezzoNetto = 0; double _sconto1 = 0; double _sconto2 = 0; double _sconto3 = 0; double _sconto4 = 0; double _sconto5 = 0; bool _updatingColli = false; bool _updatingQta = false; @override void initState() { super.initState(); if (widget.listinoCliente != null) { _selectedListino = _kListinoClienteSentinel; } else { _selectedListino = widget.idListinoDefault; if (_selectedListino != null && !widget.listini.any((l) => l.idListino == _selectedListino)) { _selectedListino = null; } } if (_selectedListino != null) { _loadPrezzo(_selectedListino!, widget.articolo.idArticolo); } } @override void dispose() { _colliCtrl.dispose(); _qtaCtrl.dispose(); _prezzoCtrl.dispose(); _sconto1Ctrl.dispose(); _sconto2Ctrl.dispose(); _sconto3Ctrl.dispose(); _sconto4Ctrl.dispose(); _sconto5Ctrl.dispose(); super.dispose(); } void _loadPrezzo(int idListino, int idaArticolo) async { if (idListino == _kListinoClienteSentinel && widget.listinoCliente != null) { final lc = widget.listinoCliente!; if (lc.prezzo != null) { setState(() => _prezzo = lc.prezzo!); _prezzoCtrl.text = _prezzo.toStringAsFixed(2); } if (lc.idSconto != null) { _loadSconti(lc.idSconto!); } else { _resetSconti(); } _calcolaPrezzoNetto(); return; } final lp = await widget.articoliDao .getListPrice(idListino, idaArticolo, widget.dataOrdine); /* final lp = widget.listiniArticolo.cast().firstWhere( (la) => la!.idListino == idListino, orElse: () => null, ); */ if (lp != null) { if (lp.prezzo != null) { setState(() => _prezzo = lp.prezzo!); _prezzoCtrl.text = _prezzo.toStringAsFixed(2); } if (lp.idSconto != null) { _loadSconti(lp.idSconto!); } else { _resetSconti(); } _calcolaPrezzoNetto(); } } Future _loadSconti(int idSconto) async { final sconto = await widget.scontiDao.findById(idSconto); setState(() { _sconto1 = sconto?.sconto1 ?? 0; _sconto2 = sconto?.sconto2 ?? 0; _sconto3 = sconto?.sconto3 ?? 0; _sconto4 = sconto?.sconto4 ?? 0; _sconto5 = sconto?.sconto5 ?? 0; }); _sconto1Ctrl.text = _sconto1 > 0 ? _sconto1.toStringAsFixed(1) : ''; _sconto2Ctrl.text = _sconto2 > 0 ? _sconto2.toStringAsFixed(1) : ''; _sconto3Ctrl.text = _sconto3 > 0 ? _sconto3.toStringAsFixed(1) : ''; _sconto4Ctrl.text = _sconto4 > 0 ? _sconto4.toStringAsFixed(1) : ''; _sconto5Ctrl.text = _sconto5 > 0 ? _sconto5.toStringAsFixed(1) : ''; } void _resetSconti() { setState(() { _sconto1 = 0; _sconto2 = 0; _sconto3 = 0; _sconto4 = 0; _sconto5 = 0; }); _sconto1Ctrl.clear(); _sconto2Ctrl.clear(); _sconto3Ctrl.clear(); _sconto4Ctrl.clear(); _sconto5Ctrl.clear(); } void _calcolaPrezzoNetto() { double n = _prezzo; n *= (100 - _sconto1) / 100; n *= (100 - _sconto2) / 100; n *= (100 - _sconto3) / 100; n *= (100 - _sconto4) / 100; n *= (100 - _sconto5) / 100; setState(() => _prezzoNetto = n); } void _onPrezzoChanged(String v) { setState(() => _prezzo = double.tryParse(v) ?? 0); _calcolaPrezzoNetto(); } void _onScontoChanged(int index, String v) { final val = double.tryParse(v) ?? 0; setState(() { switch (index) { case 1: _sconto1 = val; case 2: _sconto2 = val; case 3: _sconto3 = val; case 4: _sconto4 = val; case 5: _sconto5 = val; } }); _calcolaPrezzoNetto(); } void _onColliChanged(String v) { if (_updatingColli) return; _updatingQta = true; final colli = double.tryParse(v) ?? 0; final pezzi = widget.articolo.pezziConfezione ?? 0; if (colli > 0 && pezzi > 0) { _qtaCtrl.text = (colli * pezzi).toStringAsFixed(1); } else if (colli == 0) { _qtaCtrl.text = ''; } _updatingQta = false; } void _onQtaChanged(String v) { if (_updatingQta) return; _updatingColli = true; final qta = double.tryParse(v) ?? 0; if (qta > 0) { _colliCtrl.text = '0'; } else if (qta == 0) { _colliCtrl.text = ''; } _updatingColli = false; } void _confirm() { final colli = double.tryParse(_colliCtrl.text) ?? 0; final qta = double.tryParse(_qtaCtrl.text) ?? 0; final prezzoLordo = double.tryParse(_prezzoCtrl.text) ?? 0; final listinoId = _selectedListino == _kListinoClienteSentinel ? (widget.listinoCliente?.idListino ?? 0) : (_selectedListino ?? 0); widget.onConfirm( colli, qta, prezzoLordo, _prezzoNetto, listinoId, sconto1: _sconto1, sconto2: _sconto2, sconto3: _sconto3, sconto4: _sconto4, sconto5: _sconto5, ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, left: 16, right: 16, top: 16, ), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.articolo.descrizione ?? '', style: theme.textTheme.titleMedium, ), const SizedBox(height: 4), Text( widget.articolo.codArticolo ?? '', style: theme.textTheme.bodySmall, ), if (widget.articolo.pezziConfezione != null) Text( 'Pezzi/confezione: ${widget.articolo.pezziConfezione}', style: theme.textTheme.bodySmall, ), const SizedBox(height: 16), DropdownButtonFormField( initialValue: _selectedListino, decoration: const InputDecoration(labelText: 'Listino'), isExpanded: true, items: [ if (widget.listinoCliente != null) DropdownMenuItem( value: _kListinoClienteSentinel, child: const Text( 'Listino Cliente', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue, ), ), ), ...widget.listini.map((l) { return DropdownMenuItem( value: l.idListino, child: CodDescDropdownItem( codice: l.codListino, descrizione: l.descrizione, ), ); }), ], onChanged: (v) { setState(() => _selectedListino = v); if (v != null) _loadPrezzo(v, widget.articolo.idArticolo); }, ), const SizedBox(height: 12), TextField( controller: _prezzoCtrl, decoration: const InputDecoration(labelText: 'Prezzo lordo'), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), enabled: widget.canEditPrezzo, onChanged: _onPrezzoChanged, ), const SizedBox(height: 12), Row( children: [ Expanded( child: TextField( controller: _sconto1Ctrl, decoration: const InputDecoration(labelText: 'Sconto 1 %'), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), onChanged: (v) => _onScontoChanged(1, v), ), ), const SizedBox(width: 12), Expanded( child: TextField( controller: _sconto2Ctrl, decoration: const InputDecoration(labelText: 'Sconto 2 %'), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), onChanged: (v) => _onScontoChanged(2, v), ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: TextField( controller: _sconto3Ctrl, decoration: const InputDecoration(labelText: 'Sconto 3 %'), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), onChanged: (v) => _onScontoChanged(3, v), ), ), const SizedBox(width: 12), Expanded( child: TextField( controller: _sconto4Ctrl, decoration: const InputDecoration(labelText: 'Sconto 4 %'), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), onChanged: (v) => _onScontoChanged(4, v), ), ), ], ), const SizedBox(height: 12), SizedBox( width: 140, child: TextField( controller: _sconto5Ctrl, decoration: const InputDecoration(labelText: 'Sconto 5 %'), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), onChanged: (v) => _onScontoChanged(5, v), ), ), const SizedBox(height: 16), Row( children: [ Text('Prezzo netto: ', style: theme.textTheme.bodyLarge), Text( _prezzoNetto.toStringAsFixed(2), style: theme.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: TextField( controller: _colliCtrl, decoration: const InputDecoration(labelText: 'Colli'), keyboardType: TextInputType.number, onChanged: _onColliChanged, ), ), const SizedBox(width: 12), Expanded( child: TextField( controller: _qtaCtrl, decoration: const InputDecoration(labelText: 'Quantità'), keyboardType: TextInputType.number, onChanged: _onQtaChanged, ), ), ], ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _confirm, child: const Text('Aggiungi'), ), ), const SizedBox(height: 8), ], ), ), ); } }