// File: adjust_bank_balance_screen.dart import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:iconly/iconly.dart'; import 'package:intl/intl.dart'; import 'package:mobile_pos/constant.dart'; import 'package:mobile_pos/generated/l10n.dart' as l; // --- Local Imports --- import 'package:mobile_pos/currency.dart'; // Data Source Imports import 'package:mobile_pos/Screens/cash%20and%20bank/bank%20to%20bank%20transfer/repo/bank_to_bank_transfar_repo.dart'; import '../bank account/model/bank_transfer_history_model.dart'; import '../bank%20account/model/bank_account_list_model.dart'; import '../bank%20account/provider/bank_account_provider.dart'; import '../widgets/image_picker_widget.dart'; // Adjustment Type Model (Reused) class AdjustmentType { final String displayName; final String apiValue; const AdjustmentType(this.displayName, this.apiValue); } const List adjustmentTypes = [ AdjustmentType('Increase balance', 'credit'), AdjustmentType('Decrease balance', 'debit'), ]; class AdjustBankBalanceScreen extends ConsumerStatefulWidget { // Added optional transaction parameter for editing final TransactionData? transaction; const AdjustBankBalanceScreen({super.key, this.transaction}); @override ConsumerState createState() => _AdjustBankBalanceScreenState(); } class _AdjustBankBalanceScreenState extends ConsumerState { final GlobalKey _key = GlobalKey(); final amountController = TextEditingController(); final dateController = TextEditingController(); final descriptionController = TextEditingController(); BankData? _selectedBank; AdjustmentType? _selectedType; DateTime? _selectedDate; File? _pickedImage; String? _existingImageUrl; // State for image already on the server final DateFormat _displayFormat = DateFormat('dd/MM/yyyy'); final DateFormat _apiFormat = DateFormat('yyyy-MM-dd'); @override void initState() { super.initState(); final transaction = widget.transaction; if (transaction != null) { // Pre-fill data for editing amountController.text = transaction.amount?.toString() ?? ''; descriptionController.text = transaction.note ?? ''; _existingImageUrl = transaction.image; _selectedType = adjustmentTypes.firstWhere( (type) => type.apiValue == transaction.type, orElse: () => adjustmentTypes.first, ); try { if (transaction.date != null) { _selectedDate = _apiFormat.parse(transaction.date!); dateController.text = _displayFormat.format(_selectedDate!); } } catch (e) { _selectedDate = DateTime.now(); dateController.text = _displayFormat.format(_selectedDate!); } } else { // For a new transaction _selectedDate = DateTime.now(); dateController.text = _displayFormat.format(_selectedDate!); _selectedType = adjustmentTypes.first; } } @override void dispose() { amountController.dispose(); dateController.dispose(); descriptionController.dispose(); super.dispose(); } Future _selectDate(BuildContext context) async { final DateTime? picked = await showDatePicker( initialDate: _selectedDate ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2101), context: context, ); if (picked != null) { setState(() { _selectedDate = picked; dateController.text = _displayFormat.format(picked); }); } } // --- Submission Logic (Handles both Create and Update) --- // --- Submission Logic (Handles both Create and Update) --- void _submit() async { if (!_key.currentState!.validate()) return; if (_selectedBank == null || _selectedType == null) { ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text('Please select an account and adjustment type.'))); return; } final repo = BankTransactionRepo(); final isEditing = widget.transaction != null; final transactionId = widget.transaction?.id; // Base parameters are collected from the state final num amount = num.tryParse(amountController.text) ?? 0; final String date = _apiFormat.format(_selectedDate!); final String note = descriptionController.text.trim(); if (isEditing && transactionId != null) { // Call UPDATE function by passing EACH parameter explicitly await repo.updateBankTransfer( ref: ref, context: context, transactionId: transactionId, // Specific to UPDATE existingImageUrl: _existingImageUrl, // Specific to UPDATE // Common parameters passed explicitly fromBankId: _selectedBank!.id!, toBankId: _selectedBank!.id!, // Same bank for adjustment amount: amount, date: date, note: note, image: _pickedImage, transactionType: 'adjust_bank', type: _selectedType!.apiValue, ); } else { // Call CREATE function by passing EACH parameter explicitly await repo.createBankTransfer( ref: ref, context: context, // Common parameters passed explicitly fromBankId: _selectedBank!.id!, toBankId: _selectedBank!.id!, // Same bank for adjustment amount: amount, date: date, note: note, image: _pickedImage, transactionType: 'adjust_bank', type: _selectedType!.apiValue, ); } } // --- Form Reset Logic --- void _clearForm() { setState(() { _selectedBank = null; _selectedType = adjustmentTypes.first; _pickedImage = null; _existingImageUrl = null; _selectedDate = DateTime.now(); dateController.text = _displayFormat.format(_selectedDate!); amountController.clear(); descriptionController.clear(); }); _key.currentState?.reset(); } void _resetOrCancel(bool isResetButton) { if (isResetButton) { _clearForm(); } else { Navigator.pop(context); } } // --- Helper to pre-select bank on data load --- void _setInitialBank(List banks) { if (widget.transaction != null && _selectedBank == null) { _selectedBank = banks.firstWhere( (bank) => bank.id == widget.transaction!.fromBankId, orElse: () => _selectedBank!, ); } } @override Widget build(BuildContext context) { final _lang = l.S.of(context); final banksAsync = ref.watch(bankListProvider); final isEditing = widget.transaction != null; final appBarTitle = isEditing ? _lang.editBankAdjustment : _lang.adjustBankBalance; final saveButtonText = isEditing ? _lang.update : _lang.save; return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text(appBarTitle), centerTitle: true, elevation: 0, ), body: banksAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (err, stack) => Center(child: Text('Error loading bank accounts: $err')), data: (bankModel) { final banks = bankModel.data ?? []; // Set initial bank state once the data is loaded _setInitialBank(banks); if (banks.isEmpty) { return Center( child: Padding( padding: EdgeInsets.all(20.0), child: Text(_lang.pleaseAddAtLeastOneBank, textAlign: TextAlign.center), )); } return SingleChildScrollView( padding: const EdgeInsets.all(20), child: Form( key: _key, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 1. Account Name _buildAccountDropdown(banks), const SizedBox(height: 20), // 2. Type (Increase/Decrease) _buildAdjustmentTypeDropdown(), const SizedBox(height: 20), // 3. Amount _buildAmountInput(), const SizedBox(height: 20), // 4. Adjustment Date _buildDateInput(context), const SizedBox(height: 20), // 5. Description _buildDescriptionInput(), const SizedBox(height: 20), // 6. Image Picker (Updated for Edit) ReusableImagePicker( initialImage: _pickedImage, existingImageUrl: _existingImageUrl, onImagePicked: (file) { setState(() { _pickedImage = file; if (file != null) _existingImageUrl = null; }); }, onImageRemoved: () { setState(() { _pickedImage = null; _existingImageUrl = null; }); }, ), const SizedBox(height: 30), ], ), ), ); }, ), bottomNavigationBar: Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ Expanded( child: OutlinedButton( onPressed: () => _resetOrCancel(true), style: OutlinedButton.styleFrom( minimumSize: const Size(double.infinity, 50), side: const BorderSide(color: Colors.red), foregroundColor: Colors.red, ), child: Text(_lang.resets), ), ), const SizedBox(width: 16), Expanded( child: ElevatedButton( onPressed: _submit, style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), backgroundColor: const Color(0xFFB71C1C), foregroundColor: Colors.white, ), child: Text(saveButtonText), // Dynamically shows Save/Update ), ), ], ), ), ); } // --- Widget Builders --- Widget _buildAccountDropdown(List banks) { return DropdownButtonFormField( icon: Icon( Icons.keyboard_arrow_down, color: kPeraColor, ), value: _selectedBank, decoration: InputDecoration( labelText: l.S.of(context).accountNumber, hintText: l.S.of(context).selectOne, ), validator: (value) => value == null ? l.S.of(context).selectAccount : null, items: banks.map((bank) { return DropdownMenuItem( value: bank, child: Text(bank.name ?? 'Unknown'), ); }).toList(), onChanged: (BankData? newValue) { setState(() { _selectedBank = newValue; }); }, ); } Widget _buildAdjustmentTypeDropdown() { return DropdownButtonFormField( icon: Icon( Icons.keyboard_arrow_down, color: kPeraColor, ), value: _selectedType, decoration: InputDecoration( labelText: l.S.of(context).type, hintText: l.S.of(context).selectType, ), validator: (value) => value == null ? l.S.of(context).selectType : null, items: adjustmentTypes.map((type) { return DropdownMenuItem( value: type, child: Text(type.displayName), ); }).toList(), onChanged: (AdjustmentType? newValue) { setState(() { _selectedType = newValue; }); }, ); } Widget _buildAmountInput() { return TextFormField( controller: amountController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: l.S.of(context).amount, hintText: 'Ex: 500', prefixText: currency, ), validator: (value) { if (value!.isEmpty) return l.S.of(context).amountsIsRequired; if (num.tryParse(value) == null || num.parse(value) <= 0) return l.S.of(context).invalidAmount; return null; }, ); } Widget _buildDateInput(BuildContext context) { return TextFormField( readOnly: true, controller: dateController, decoration: InputDecoration( labelText: l.S.of(context).adjustmentDate, hintText: 'DD/MM/YYYY', suffixIcon: IconButton( icon: const Icon(IconlyLight.calendar, size: 22), onPressed: () => _selectDate(context), ), ), validator: (value) => value!.isEmpty ? l.S.of(context).dateIsRequired : null, ); } Widget _buildDescriptionInput() { return TextFormField( controller: descriptionController, maxLines: 3, decoration: InputDecoration( labelText: l.S.of(context).description, hintText: l.S.of(context).enterDescription, contentPadding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 10.0), ), ); } }