import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.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'; 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; bool _usaListinoCliente = true; Map _mapListini = {}; @override void initState() { super.initState(); _loadListini(); _searchArticles(''); } @override void dispose() { _artCtrl.dispose(); super.dispose(); } Future _loadListini() async { final listini = await ref.read(listiniDaoProvider).listSenzaFiltro(); setState(() { _mapListini = {for (final l in listini) l.idListino: l}; }); } Future _searchArticles(String q) async { setState(() => _searching = true); final dao = ref.read(articoliDaoProvider); List results; if (_usaListinoCliente && widget.idCliente != 0) { results = await dao.getArticoliPerListinoCliente( widget.idCliente, widget.dataOrdine, ); } else { results = await dao.getArticoliPerListinoAttivo(widget.dataOrdine, q); } if (q.isNotEmpty) { final lower = q.toLowerCase(); results = results.where((a) { final cod = (a.codArticolo ?? '').toLowerCase(); final desc = (a.descrizione ?? '').toLowerCase(); final ean = (a.ean ?? '').toLowerCase(); return cod.contains(lower) || desc.contains(lower) || ean.contains(lower); }).toList(); } setState(() { _searchResults = results; _searching = false; }); } void _showArticlePicker(Articolo art) async { final agente = ref.read(authProvider).agente; final canEditPrice = agente?.modificaPrezzi == 1; final listini = await ref .read(articoliDaoProvider) .getListiniArticoloValidi(art.idArticolo, widget.dataOrdine); /* final seen = {}; final listini = listiniArticolo .map((la) => _mapListini[la.idListino]) .whereType() .where((l) => seen.add(l.idListino)) .toList(); */ final scontiDao = ref.read(scontiDaoProvider); final articoliDao = ref.read(articoliDaoProvider); if (!context.mounted) return; final sheetResult = await showModalBottomSheet( context: context, isScrollControlled: true, builder: (_) => _ArticlePickerSheet( articolo: art, listini: listini, 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) { return Scaffold( appBar: AppBar( title: const Text('Selezione articolo'), ), body: SafeArea( child: Column( children: [ Padding( padding: const EdgeInsets.all(12), child: Row( children: [ Expanded( 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, ), ), const SizedBox(width: 8), FilterChip( label: const Text('Listino cliente'), selected: _usaListinoCliente, onSelected: (v) { setState(() => _usaListinoCliente = v); _searchArticles(_artCtrl.text); }, ), ], ), ), Expanded( child: _searchResults.isEmpty ? Center( child: Text( _searching ? 'Ricerca...' : 'Nessun articolo trovato', ), ) : ListView.builder( itemCount: _searchResults.length, itemBuilder: (_, i) => ListTile( leading: ArticleImage( idArticolo: _searchResults[i].idArticolo, codArticolo: _searchResults[i].codArticolo, ), title: Text(_searchResults[i].descrizione ?? ''), subtitle: Text(_searchResults[i].codArticolo ?? ''), trailing: TextButton( onPressed: () => _showArticlePicker(_searchResults[i]), child: const Text('Aggiungi'), ), ), ), ), ], ), ), ); } } class _ArticlePickerSheet extends StatefulWidget { final Articolo articolo; // final List listiniArticolo; final List listini; 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, 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(); _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 { 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 ?? 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: widget.listini.map((l) { print(l); return DropdownMenuItem( value: l.idListino, child: CodDescDropdownItem( codice: l.codListino, descrizione: l.descrizione, ), ); }).toList(), 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), ], ), ), ); } }