From 200e3d1ac86972559d79e9a6587a2104b906c9b9 Mon Sep 17 00:00:00 2001 From: QUOC HAI Date: Sat, 21 Jun 2025 14:34:44 +0700 Subject: [PATCH] feat: finish thamSo Screen --- lib/features/adjust/bloc/tham_so_bloc.dart | 34 +++ lib/features/adjust/bloc/tham_so_event.dart | 12 + lib/features/adjust/bloc/tham_so_state.dart | 19 ++ lib/features/adjust/tham_so_api.dart | 28 +++ lib/features/adjust/tham_so_repository.dart | 24 ++ .../bloc/phieu_ban_hang_bloc.dart | 8 +- lib/main.dart | 11 + lib/models/tham_so.dart | 27 ++ lib/screens/home/home_screen.dart | 27 +- .../quanlythongtin/loai_dich_vu_screen.dart | 21 +- .../home/view_list/tham_so_screen.dart | 237 ++++++++++++++++++ .../phieumuaban_create_dialog.dart | 21 +- .../reusable_widgets/qltt_create_dialog.dart | 4 +- .../reusable_table_widget.dart | 55 ++-- 14 files changed, 484 insertions(+), 44 deletions(-) create mode 100644 lib/features/adjust/bloc/tham_so_bloc.dart create mode 100644 lib/features/adjust/bloc/tham_so_event.dart create mode 100644 lib/features/adjust/bloc/tham_so_state.dart create mode 100644 lib/features/adjust/tham_so_api.dart create mode 100644 lib/features/adjust/tham_so_repository.dart create mode 100644 lib/models/tham_so.dart create mode 100644 lib/screens/home/view_list/tham_so_screen.dart diff --git a/lib/features/adjust/bloc/tham_so_bloc.dart b/lib/features/adjust/bloc/tham_so_bloc.dart new file mode 100644 index 0000000..998f8fb --- /dev/null +++ b/lib/features/adjust/bloc/tham_so_bloc.dart @@ -0,0 +1,34 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gemstore_frontend/features/adjust/bloc/tham_so_event.dart'; +import 'package:gemstore_frontend/features/adjust/bloc/tham_so_state.dart'; +import 'package:gemstore_frontend/features/adjust/tham_so_repository.dart'; + +class ThamSoBloc extends Bloc { + final ThamSoRepository thamSoRepository; + + ThamSoBloc(this.thamSoRepository) : super(ThamSoStateInitial()) { + on((event, emit) { + emit(ThamSoStateInitial()); + }); + + on((event, emit) async { + emit(ThamSoStateLoading()); + try { + final thamSoList = await thamSoRepository.getAll(); + emit(ThamSoStateLoaded(thamSoList)); + } catch (e) { + emit(ThamSoStateError('Failed to get all tham so: $e')); + } + }); + + on((event, emit) async { + emit(ThamSoStateLoading()); + try { + await thamSoRepository.updateThamSo(event.tenThamSo, event.giaTri); + add(ThamSoEventGetAll()); + } catch (e) { + emit(ThamSoStateError('Failed to update tham so: $e')); + } + }); + } +} diff --git a/lib/features/adjust/bloc/tham_so_event.dart b/lib/features/adjust/bloc/tham_so_event.dart new file mode 100644 index 0000000..548ae4e --- /dev/null +++ b/lib/features/adjust/bloc/tham_so_event.dart @@ -0,0 +1,12 @@ +class ThamSoEvent {} + +class ThamSoEventStart extends ThamSoEvent {} + +class ThamSoEventGetAll extends ThamSoEvent {} + +class ThamSoEventUpdate extends ThamSoEvent { + final String tenThamSo; + final String giaTri; + + ThamSoEventUpdate(this.tenThamSo, this.giaTri); +} \ No newline at end of file diff --git a/lib/features/adjust/bloc/tham_so_state.dart b/lib/features/adjust/bloc/tham_so_state.dart new file mode 100644 index 0000000..3805402 --- /dev/null +++ b/lib/features/adjust/bloc/tham_so_state.dart @@ -0,0 +1,19 @@ +import 'package:gemstore_frontend/models/tham_so.dart'; + +sealed class ThamSoState {} + +class ThamSoStateInitial extends ThamSoState {} + +class ThamSoStateLoading extends ThamSoState {} + +class ThamSoStateLoaded extends ThamSoState { + final List thamSoList; + + ThamSoStateLoaded(this.thamSoList); +} + +class ThamSoStateError extends ThamSoState { + final String message; + + ThamSoStateError(this.message); +} diff --git a/lib/features/adjust/tham_so_api.dart b/lib/features/adjust/tham_so_api.dart new file mode 100644 index 0000000..9bea6c3 --- /dev/null +++ b/lib/features/adjust/tham_so_api.dart @@ -0,0 +1,28 @@ +import 'package:dio/dio.dart'; +import 'package:gemstore_frontend/models/tham_so.dart'; + +class ThamSoApi { + final Dio dio; + + ThamSoApi(this.dio); + + Future> getAll() async { + try { + final response = await dio.get('/api/thamso'); + return ThamSo.fromJsonList(response.data); + } catch (e) { + throw Exception('Error fetching tham so: $e'); + } + } + + Future updateThamSo(String tenThamSo, String giaTri) async { + try { + await dio.post('/api/thamso', data: { + 'tenThamSo': tenThamSo, + 'giaTri': giaTri, + }); + } catch (e) { + throw Exception('Error updating tham so: $e'); + } + } +} diff --git a/lib/features/adjust/tham_so_repository.dart b/lib/features/adjust/tham_so_repository.dart new file mode 100644 index 0000000..22dfc0d --- /dev/null +++ b/lib/features/adjust/tham_so_repository.dart @@ -0,0 +1,24 @@ +import 'package:gemstore_frontend/features/adjust/tham_so_api.dart'; +import 'package:gemstore_frontend/models/tham_so.dart'; + +class ThamSoRepository { + final ThamSoApi thamSoApi; + + ThamSoRepository(this.thamSoApi); + + Future> getAll() async { + try { + return await thamSoApi.getAll(); + } catch (e) { + throw Exception('Error fetching tham so: $e'); + } + } + + Future updateThamSo(String tenThamSo, String giaTri) async { + try { + await thamSoApi.updateThamSo(tenThamSo, giaTri); + } catch (e) { + throw Exception('Error updating tham so: $e'); + } + } +} \ No newline at end of file diff --git a/lib/features/home/phieu_ban_hang/bloc/phieu_ban_hang_bloc.dart b/lib/features/home/phieu_ban_hang/bloc/phieu_ban_hang_bloc.dart index c8d1fd5..143072a 100644 --- a/lib/features/home/phieu_ban_hang/bloc/phieu_ban_hang_bloc.dart +++ b/lib/features/home/phieu_ban_hang/bloc/phieu_ban_hang_bloc.dart @@ -28,7 +28,7 @@ class PhieuBanHangBloc extends Bloc { await phieuBanHangRepository.create(event.khachHang, event.sanPhamBan); add(PhieuBanHangEventGetAll()); } catch (e) { - emit(PhieuBanHangStateFailure("Lỗi tạo phiếu bán hàng: ${e.toString()}")); + emit(PhieuBanHangStateFailure("- Lỗi tạo: Số lượng bán không hợp lệ")); } } @@ -43,7 +43,7 @@ class PhieuBanHangBloc extends Bloc { } catch (e) { emit( PhieuBanHangStateFailure( - "Lỗi lấy danh sách phiếu bán hàng: ${e.toString()}", + "Lỗi lấy danh sách: ${e.toString()}", ), ); } @@ -58,7 +58,7 @@ class PhieuBanHangBloc extends Bloc { await phieuBanHangRepository.delete(event.soPhieu); add(PhieuBanHangEventGetAll()); } catch (e) { - emit(PhieuBanHangStateFailure("Lỗi xóa phiếu bán hàng: ${e.toString()}")); + emit(PhieuBanHangStateFailure("Lỗi xóa: ${e.toString()}")); } } @@ -77,7 +77,7 @@ class PhieuBanHangBloc extends Bloc { } catch (e) { emit( PhieuBanHangStateFailure( - "Lỗi cập nhật phiếu bán hàng: ${e.toString()}", + "- Lỗi cập nhật: Số lượng bán không hợp lệ", ), ); } diff --git a/lib/main.dart b/lib/main.dart index feca68d..3f9076c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gemstore_frontend/config/http_client.dart'; import 'package:gemstore_frontend/config/router.dart'; +import 'package:gemstore_frontend/features/adjust/bloc/tham_so_bloc.dart'; +import 'package:gemstore_frontend/features/adjust/bloc/tham_so_event.dart'; +import 'package:gemstore_frontend/features/adjust/tham_so_api.dart'; +import 'package:gemstore_frontend/features/adjust/tham_so_repository.dart'; import 'package:gemstore_frontend/features/home/don_vi_tinh/bloc/don_vi_tinh_bloc.dart'; import 'package:gemstore_frontend/features/home/don_vi_tinh/bloc/don_vi_tinh_event.dart'; import 'package:gemstore_frontend/features/home/don_vi_tinh/don_vi_tinh_api.dart'; @@ -77,6 +81,9 @@ class MyApp extends StatelessWidget { RepositoryProvider( create: (context) => BaoCaoRepository(BaoCaoApi(dio)), ), + RepositoryProvider( + create: (context) => ThamSoRepository(ThamSoApi(dio)), + ), ], child: MultiBlocProvider( providers: [ @@ -118,6 +125,9 @@ class MyApp extends StatelessWidget { BlocProvider( create: (context) => BaoCaoBloc(context.read()), ), + BlocProvider( + create: (context) => ThamSoBloc(context.read()), + ), ], child: AppContent(), ), @@ -145,6 +155,7 @@ class _AppContentState extends State { context.read().add(PhieuDichVuEventStart()); context.read().add(SanPhamEventStart()); context.read().add(BaoCaoEventStart()); + context.read().add(ThamSoEventStart()); } @override diff --git a/lib/models/tham_so.dart b/lib/models/tham_so.dart new file mode 100644 index 0000000..49723d4 --- /dev/null +++ b/lib/models/tham_so.dart @@ -0,0 +1,27 @@ +class ThamSo { + String tenThamSo; + int giaTri; + + ThamSo({ + required this.tenThamSo, + required this.giaTri, + }); + + factory ThamSo.fromJson(Map json) { + return ThamSo( + tenThamSo: json['tenThamSo'], + giaTri: json['giaTri'], + ); + } + + static List fromJsonList(List jsonList) { + return jsonList.map((json) => ThamSo.fromJson(json as Map)).toList(); + } + + Map toJson() { + return { + 'tenThamSo': tenThamSo, + 'giaTri': giaTri, + }; + } +} diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index d92998f..5fbbdcc 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gemstore_frontend/config/format.dart'; +import 'package:gemstore_frontend/features/adjust/bloc/tham_so_bloc.dart'; +import 'package:gemstore_frontend/features/adjust/bloc/tham_so_event.dart'; +import 'package:gemstore_frontend/features/adjust/bloc/tham_so_state.dart'; import 'package:gemstore_frontend/features/home/don_vi_tinh/bloc/don_vi_tinh_bloc.dart'; import 'package:gemstore_frontend/features/home/don_vi_tinh/bloc/don_vi_tinh_event.dart'; import 'package:gemstore_frontend/features/home/don_vi_tinh/bloc/don_vi_tinh_state.dart'; @@ -33,6 +36,7 @@ import 'package:gemstore_frontend/models/phieu_ban_hang.dart'; import 'package:gemstore_frontend/models/phieu_dich_vu.dart'; import 'package:gemstore_frontend/models/phieu_mua_hang.dart'; import 'package:gemstore_frontend/models/san_pham.dart'; +import 'package:gemstore_frontend/models/tham_so.dart'; import 'package:gemstore_frontend/screens/home/view_list/bao_cao_screen.dart'; import 'package:gemstore_frontend/screens/home/view_list/danh_sach_san_pham_screen.dart'; import 'package:gemstore_frontend/screens/home/view_list/phieunhapxuat/phieu_ban_hang_screen.dart'; @@ -42,6 +46,7 @@ import 'package:gemstore_frontend/screens/home/view_list/quanlythongtin/don_vi_t import 'package:gemstore_frontend/screens/home/view_list/quanlythongtin/loai_dich_vu_screen.dart'; import 'package:gemstore_frontend/screens/home/view_list/quanlythongtin/loai_san_pham_screen.dart'; import 'package:gemstore_frontend/screens/home/view_list/quanlythongtin/nha_cung_cap_screen.dart'; +import 'package:gemstore_frontend/screens/home/view_list/tham_so_screen.dart'; import 'package:gemstore_frontend/screens/reusable_widgets/error_dialog.dart'; import 'package:go_router/go_router.dart'; @@ -71,6 +76,7 @@ class _HomeScreenState extends State { List _phieuBanHangs = []; List _phieuDichVus = []; List _sanPhams = []; + List _thamSos = []; @override void initState() { @@ -174,6 +180,17 @@ class _HomeScreenState extends State { } }, ), + BlocListener( + listener: (context, state) { + if (state is ThamSoStateLoaded) { + setState(() { + _thamSos = state.thamSoList; + }); + } else if (state is ThamSoStateError) { + _handleError("Lỗi tham số: ${state.message}"); + } + }, + ), ], child: Scaffold( body: SafeArea( @@ -318,6 +335,11 @@ class _HomeScreenState extends State { // Reports Section _buildMenuItemWithDot('Báo cáo', 'reports'), + + const SizedBox(height: 16.0), + + // Reports Section + _buildMenuItemWithDot('Điều chỉnh tham số', 'adjust_parameters'), ], ), ), @@ -573,7 +595,7 @@ class _HomeScreenState extends State { return LoaiSanPhamScreen(data: _loaiSanPhams, listDonViTinh: _donViTinhs); case 'service_type_management': - return LoaiDichVuScreen(data: _loaiDichVus); + return LoaiDichVuScreen(data: _loaiDichVus, thamSo: _thamSos); case 'sales_invoice': return PhieuBanHangScreen( data: _phieuBanHangs, @@ -599,6 +621,8 @@ class _HomeScreenState extends State { _phieuBanHangs, ) ); + case 'adjust_parameters': + return ThamSoScreen(thamSoList: _thamSos); default: return Column( mainAxisAlignment: MainAxisAlignment.center, @@ -626,5 +650,6 @@ class _HomeScreenState extends State { context.read().add(PhieuMuaHangEventGetAll()); context.read().add(PhieuBanHangEventGetAll()); context.read().add(PhieuDichVuEventGetAll()); + context.read().add(ThamSoEventGetAll()); } } diff --git a/lib/screens/home/view_list/quanlythongtin/loai_dich_vu_screen.dart b/lib/screens/home/view_list/quanlythongtin/loai_dich_vu_screen.dart index 533d7a2..c6452ce 100644 --- a/lib/screens/home/view_list/quanlythongtin/loai_dich_vu_screen.dart +++ b/lib/screens/home/view_list/quanlythongtin/loai_dich_vu_screen.dart @@ -4,20 +4,29 @@ import 'package:gemstore_frontend/features/home/loai_dich_vu/bloc/loai_dich_vu_b import 'package:gemstore_frontend/features/home/loai_dich_vu/bloc/loai_dich_vu_event.dart'; import 'package:gemstore_frontend/features/home/loai_dich_vu/bloc/loai_dich_vu_state.dart'; import 'package:gemstore_frontend/models/loai_dich_vu.dart'; +import 'package:gemstore_frontend/models/tham_so.dart'; import 'package:gemstore_frontend/screens/reusable_widgets/format_column_data.dart'; import 'package:gemstore_frontend/screens/reusable_widgets/qltt_create_dialog.dart'; import 'package:gemstore_frontend/screens/reusable_widgets/reusable_table_widget.dart'; class LoaiDichVuScreen extends StatefulWidget { final List data; - const LoaiDichVuScreen({super.key, required this.data}); + final List thamSo; + + const LoaiDichVuScreen({super.key, required this.data, required this.thamSo}); @override State createState() => _LoaiDichVuScreenState(); } class _LoaiDichVuScreenState extends State { - final List _columns = [ + late List _columns; + bool _isLoading = false; + + @override + void initState() { + super.initState(); + _columns = [ TableColumn( key: 'id', header: 'Mã loại dịch vụ', @@ -47,13 +56,11 @@ class _LoaiDichVuScreenState extends State { (value) => !(double.tryParse(value) == null || double.tryParse(value)! < 0), errorMessage: 'Phần trăm trả trước phải lớn hơn hoặc bằng 0', + defaultData: widget.thamSo + .firstWhere((thamSo) => thamSo.tenThamSo == 'TiLeTraTruocMacDinh') + .giaTri.toString(), ), ]; - bool _isLoading = false; - - @override - void initState() { - super.initState(); } void _showAddLoaiDichVuDialog() { diff --git a/lib/screens/home/view_list/tham_so_screen.dart b/lib/screens/home/view_list/tham_so_screen.dart new file mode 100644 index 0000000..a8f13d1 --- /dev/null +++ b/lib/screens/home/view_list/tham_so_screen.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:gemstore_frontend/models/tham_so.dart'; + +class ThamSoScreen extends StatefulWidget { + final List thamSoList; + final Function(List)? onUpdate; // Callback để cập nhật dữ liệu + + const ThamSoScreen({ + super.key, + required this.thamSoList, + this.onUpdate, + }); + + @override + State createState() => _ThamSoScreenState(); +} + +class _ThamSoScreenState extends State { + late List _thamSoList; + late List _controllers; + final _formKey = GlobalKey(); + bool _isEditing = false; + + @override + void initState() { + super.initState(); + // Tạo bản sao của danh sách để có thể chỉnh sửa + _thamSoList = List.from(widget.thamSoList); + + // Khởi tạo controllers cho mỗi tham số + _controllers = _thamSoList.map((thamSo) => + TextEditingController(text: thamSo.giaTri.toString()) + ).toList(); + } + + @override + void dispose() { + // Giải phóng memory của controllers + for (var controller in _controllers) { + controller.dispose(); + } + super.dispose(); + } + + // Hàm để hiển thị tên tham số dễ đọc + String _getDisplayName(String tenThamSo) { + switch (tenThamSo) { + case 'SoLuongTonToiThieu': + return 'Số lượng tồn tối thiểu (sản phẩm)'; + case 'TiLeTraTruocMacDinh': + return 'Tỷ lệ trả trước mặc định (%)'; + case 'SoNgayGiaoToiDa': + return 'Số ngày giao hàng tối đa (ngày)'; + default: + return tenThamSo; + } + } + + // Hàm để lấy đơn vị của tham số + String _getUnit(String tenThamSo) { + switch (tenThamSo) { + case 'SoLuongTonToiThieu': + return 'sản phẩm'; + case 'TiLeTraTruocMacDinh': + return '%'; + case 'SoNgayGiaoToiDa': + return 'ngày'; + default: + return ''; + } + } + + // Validator cho các ô nhập + String? _validateGiaTri(String? value) { + if (value == null || value.isEmpty) { + return 'Vui lòng nhập giá trị'; + } + + final number = int.tryParse(value); + if (number == null) { + return 'Vui lòng nhập số nguyên hợp lệ'; + } + + if (number <= 0) { + return 'Giá trị phải lớn hơn 0'; + } + + return null; + } + + // Hàm lưu các thay đổi + void _saveChanges() { + if (_formKey.currentState!.validate()) { + // Cập nhật giá trị trong danh sách + for (int i = 0; i < _thamSoList.length; i++) { + _thamSoList[i].giaTri = int.parse(_controllers[i].text).toInt(); + } + + // Gọi callback để cập nhật dữ liệu ở parent widget + if (widget.onUpdate != null) { + widget.onUpdate!(_thamSoList); + } + + setState(() { + _isEditing = false; + }); + + // Hiển thị thông báo thành công + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Cập nhật thành công!'), + backgroundColor: Colors.green, + ), + ); + } + } + + // Hàm hủy thay đổi + void _cancelChanges() { + // Reset controllers về giá trị ban đầu + for (int i = 0; i < _controllers.length; i++) { + _controllers[i].text = widget.thamSoList[i].giaTri.toString(); + } + + setState(() { + _isEditing = false; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + actions: [ + if (!_isEditing) + IconButton( + icon: const Icon(Icons.edit), + onPressed: () { + setState(() { + _isEditing = true; + }); + }, + tooltip: 'Chỉnh sửa', + ), + if (_isEditing) ...[ + IconButton( + icon: const Icon(Icons.close), + onPressed: _cancelChanges, + tooltip: 'Hủy', + ), + IconButton( + icon: const Icon(Icons.check), + onPressed: _saveChanges, + tooltip: 'Lưu', + ), + ], + ], + ), + body: Form( + key: _formKey, + child: ListView.builder( + padding: const EdgeInsets.all(16.0), + itemCount: _thamSoList.length, + itemBuilder: (context, index) { + final thamSo = _thamSoList[index]; + + return Card( + margin: const EdgeInsets.only(bottom: 12.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _getDisplayName(thamSo.tenThamSo), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + _getDisplayName(thamSo.tenThamSo), + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.grey[600], + ), + ), + const SizedBox(height: 12), + if (_isEditing) + TextFormField( + controller: _controllers[index], + decoration: InputDecoration( + labelText: 'Giá trị', + border: const OutlineInputBorder(), + prefixIcon: const Icon(Icons.numbers), + suffixText: _getUnit(thamSo.tenThamSo), + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + validator: _validateGiaTri, + ) + else + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 16, + ), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey[300]!), + borderRadius: BorderRadius.circular(4), + color: Colors.grey[50], + ), + child: Row( + children: [ + const Icon(Icons.numbers, color: Colors.grey), + const SizedBox(width: 12), + Text( + 'Giá trị: ${thamSo.giaTri.toInt()} ${_getUnit(thamSo.tenThamSo)}', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ), + ], + ), + ), + ); + }, + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/reusable_widgets/phieumuaban_create_dialog.dart b/lib/screens/reusable_widgets/phieumuaban_create_dialog.dart index 16deead..64bd224 100644 --- a/lib/screens/reusable_widgets/phieumuaban_create_dialog.dart +++ b/lib/screens/reusable_widgets/phieumuaban_create_dialog.dart @@ -29,6 +29,7 @@ class _PhieumuahangCreateDialogState extends State { String? tenLSP; String? donViTinhSP; String? donGiaMuaSP; + int? soLuongHienTai; List> addedItems = []; @override @@ -87,6 +88,7 @@ class _PhieumuahangCreateDialogState extends State { tenLSP = null; donViTinhSP = null; donGiaMuaSP = null; + soLuongHienTai = null; soLuongController.clear(); }); } @@ -297,9 +299,16 @@ class _PhieumuahangCreateDialogState extends State { selectedProduct['loaiSanPham']['tenLSP']; donViTinhSP = selectedProduct['loaiSanPham']['donViTinh']['tenDonVi']; - donGiaMuaSP = widget.listNhaCungCap != null - ? selectedProduct['donGia'].toString() - : (selectedProduct['donGia'] * (100 + (selectedProduct['loaiSanPham']['loiNhuan'] ?? 0)) / 100).toString(); + donGiaMuaSP = + widget.listNhaCungCap != null + ? selectedProduct['donGia'].toString() + : (selectedProduct['donGia'] * + (100 + + (selectedProduct['loaiSanPham']['loiNhuan'] ?? + 0)) / + 100) + .toString(); + soLuongHienTai = selectedProduct['tonKho'] ?? 0; }); }, ), @@ -326,15 +335,15 @@ class _PhieumuahangCreateDialogState extends State { style: const TextStyle( fontWeight: FontWeight.w500, ), - ), + ), Text( - 'Đơn Vị Tính: $donViTinhSP', + 'Đơn Giá: ${Format.moneyFormat(double.tryParse(donGiaMuaSP ?? '0')?.toInt() ?? 0)}', style: const TextStyle( fontWeight: FontWeight.w500, ), ), Text( - 'Đơn Giá: ${Format.moneyFormat(double.tryParse(donGiaMuaSP ?? '0')?.toInt() ?? 0)}', + 'Tồn kho: $soLuongHienTai ${donViTinhSP ?? ''}', style: const TextStyle( fontWeight: FontWeight.w500, ), diff --git a/lib/screens/reusable_widgets/qltt_create_dialog.dart b/lib/screens/reusable_widgets/qltt_create_dialog.dart index eedb2b0..9f1b5de 100644 --- a/lib/screens/reusable_widgets/qltt_create_dialog.dart +++ b/lib/screens/reusable_widgets/qltt_create_dialog.dart @@ -29,7 +29,9 @@ class _QlttCreateDialogState extends State { _controllers = { for (var column in widget.columns) if (column.editable && !(column.isForeignKey)) - column.key: TextEditingController(), + column.key: column.defaultData != null + ? TextEditingController(text: column.defaultData) + : TextEditingController(), }; _dropdownValues = { for (var column in widget.columns) diff --git a/lib/screens/reusable_widgets/reusable_table_widget.dart b/lib/screens/reusable_widgets/reusable_table_widget.dart index 559806f..b392fba 100644 --- a/lib/screens/reusable_widgets/reusable_table_widget.dart +++ b/lib/screens/reusable_widgets/reusable_table_widget.dart @@ -49,6 +49,7 @@ class TableColumn { final bool isForeignKey; final ForeignKeyConfig? foreignKeyConfig; final String? nestedPath; // For nested data like "loaiSanPham.tenLSP" + final String? defaultData; TableColumn({ required this.key, @@ -61,6 +62,7 @@ class TableColumn { this.isForeignKey = false, this.foreignKeyConfig, this.nestedPath, + this.defaultData, }); } @@ -136,10 +138,12 @@ class _ReusableTableWidgetState extends State { _isSearching = false; } else { _isSearching = true; - _filteredData = widget.data.where((item) { - final searchValue = item.data[widget.searchField]?.toString().toLowerCase() ?? ''; - return searchValue.contains(query.toLowerCase()); - }).toList(); + _filteredData = + widget.data.where((item) { + final searchValue = + item.data[widget.searchField]?.toString().toLowerCase() ?? ''; + return searchValue.contains(query.toLowerCase()); + }).toList(); } }); } @@ -353,27 +357,26 @@ class _ReusableTableWidgetState extends State { child: TextField( controller: _searchController, decoration: InputDecoration( - hintText: 'Tìm kiếm theo ${widget.columns.firstWhere((col) => col.key == widget.searchField).header.toLowerCase()}...', - hintStyle: TextStyle( - color: Colors.grey[500], - fontSize: 14, - ), + hintText: + 'Tìm kiếm theo ${widget.columns.firstWhere((col) => col.key == widget.searchField).header.toLowerCase()}...', + hintStyle: TextStyle(color: Colors.grey[500], fontSize: 14), prefixIcon: Icon( Icons.search, color: Colors.grey[500], size: 20, ), - suffixIcon: _isSearching - ? IconButton( - icon: Icon( - Icons.clear, - color: Colors.grey[600], - size: 20, - ), - onPressed: _clearSearch, - splashRadius: 15, - ) - : null, + suffixIcon: + _isSearching + ? IconButton( + icon: Icon( + Icons.clear, + color: Colors.grey[600], + size: 20, + ), + onPressed: _clearSearch, + splashRadius: 15, + ) + : null, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric( horizontal: 16, @@ -400,7 +403,7 @@ class _ReusableTableWidgetState extends State { children: [ // Search bar _buildSearchBar(), - + // Count display at top right Container( width: double.infinity, @@ -442,7 +445,7 @@ class _ReusableTableWidgetState extends State { ) else const SizedBox(), - + // Total count Container( padding: const EdgeInsets.symmetric( @@ -562,13 +565,15 @@ class _ReusableTableWidgetState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( - _isSearching ? Icons.search_off : Icons.inbox, + _isSearching + ? Icons.search_off + : Icons.inbox, size: 48, color: Colors.grey[400], ), const SizedBox(height: 12), Text( - _isSearching + _isSearching ? 'Không tìm thấy kết quả nào' : 'Không có dữ liệu', style: TextStyle( @@ -726,4 +731,4 @@ class _ReusableTableWidgetState extends State { }, ); } -} \ No newline at end of file +}