Files
kulakpos_app/lib/Screens/Due Calculation/due_collection_screen.dart
2026-02-07 15:57:09 +07:00

476 lines
22 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/Screens/Due%20Calculation/Model/due_collection_model.dart';
import 'package:mobile_pos/Screens/Due%20Calculation/Repo/due_repo.dart';
import 'package:mobile_pos/Screens/invoice_details/due_invoice_details.dart';
import 'package:mobile_pos/core/theme/_app_colors.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import 'package:nb_utils/nb_utils.dart';
import '../../GlobalComponents/glonal_popup.dart';
import '../../Provider/profile_provider.dart';
import '../../constant.dart';
import '../../currency.dart';
import '../../widgets/multipal payment mathods/multi_payment_widget.dart';
import '../Customers/Model/parties_model.dart';
import 'Model/due_collection_invoice_model.dart';
import 'Providers/due_provider.dart';
class DueCollectionScreen extends StatefulWidget {
const DueCollectionScreen({super.key, required this.customerModel});
@override
State<DueCollectionScreen> createState() => _DueCollectionScreenState();
final Party customerModel;
}
class _DueCollectionScreenState extends State<DueCollectionScreen> {
// Key for MultiPaymentWidget
final GlobalKey<MultiPaymentWidgetState> paymentWidgetKey = GlobalKey();
num paidAmount = 0;
num remainDueAmount = 0;
num dueAmount = 0;
num calculateDueAmount({required num total}) {
if (total < 0) {
remainDueAmount = 0;
} else {
remainDueAmount = dueAmount - total;
}
return dueAmount - total;
}
TextEditingController paidText = TextEditingController();
TextEditingController dateController = TextEditingController(text: DateTime.now().toString().substring(0, 10));
DateTime selectedDate = DateTime.now();
SalesDuesInvoice? selectedInvoice;
// int? paymentType; // Removed old single payment type
// List of items in our dropdown menu
int count = 0;
@override
void initState() {
super.initState();
// Listener to update state when paidText changes (either manually or via MultiPaymentWidget)
paidText.addListener(() {
if (paidText.text.isEmpty) {
if (mounted) {
setState(() {
paidAmount = 0;
});
}
} else {
final val = double.tryParse(paidText.text) ?? 0;
// Validation: Cannot pay more than due
if (val <= dueAmount) {
if (mounted) {
setState(() {
paidAmount = val;
});
}
} else {
// If widget pushes value > due, or user types > due
// You might want to handle this gracefully.
// For now, keeping your old logic:
paidText.clear();
if (mounted) {
setState(() {
paidAmount = 0;
});
}
EasyLoading.showError(lang.S.of(context).youCanNotPayMoreThenDue);
}
}
});
}
@override
Widget build(BuildContext context) {
count++;
return Consumer(builder: (context, consumerRef, __) {
final personalData = consumerRef.watch(businessInfoProvider);
final dueInvoiceData = consumerRef.watch(dueInvoiceListProvider(widget.customerModel.id?.round() ?? 0));
final _theme = Theme.of(context);
return personalData.when(data: (data) {
List<SalesDuesInvoice> items = [];
num openingDueAmount = 0;
return GlobalPopup(
child: Scaffold(
backgroundColor: kWhite,
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
lang.S.of(context).collectDue,
),
centerTitle: true,
iconTheme: const IconThemeData(color: Colors.black),
elevation: 0.0,
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16),
child: Column(
children: [
Row(
children: [
dueInvoiceData.when(data: (data) {
num totalDueInInvoice = 0;
if (data.salesDues?.isNotEmpty ?? false) {
for (var element in data.salesDues!) {
totalDueInInvoice += element.dueAmount ?? 0;
items.add(element);
}
}
openingDueAmount = (data.due ?? 0) - totalDueInInvoice;
if (selectedInvoice == null) {
dueAmount = openingDueAmount;
}
return Expanded(
child: DropdownButtonFormField<SalesDuesInvoice>(
isExpanded: true,
value: selectedInvoice,
hint: Text(
lang.S.of(context).selectAInvoice,
),
icon: selectedInvoice != null
? GestureDetector(
onTap: () {
setState(() {
selectedInvoice = null;
// Reset payment widget when invoice is cleared
// paymentWidgetKey.currentState?.clear();
});
},
child: const Icon(
Icons.close,
color: Colors.red,
size: 16,
),
)
: const Icon(Icons.keyboard_arrow_down, color: kGreyTextColor),
items: items.map((SalesDuesInvoice invoice) {
return DropdownMenuItem(
value: invoice,
child: Text(
invoice.invoiceNumber.toString(),
style: _theme.textTheme.bodyMedium,
),
);
}).toList(),
onChanged: (newValue) {
setState(() {
dueAmount = newValue?.dueAmount ?? 0;
paidAmount = 0;
paidText.clear();
selectedInvoice = newValue;
// Reset payment widget when invoice changes
// paymentWidgetKey.currentState?.clear();
});
},
decoration: const InputDecoration(),
),
);
}, error: (e, stack) {
return Text(e.toString());
}, loading: () {
return const Center(child: CircularProgressIndicator());
}),
const SizedBox(width: 14),
Expanded(
child: TextFormField(
keyboardType: TextInputType.name,
readOnly: true,
controller: dateController,
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: lang.S.of(context).date,
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () async {
final DateTime? picked = await showDatePicker(
initialDate: DateTime.now(),
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
context: context,
);
if (picked != null) {
setState(() {
selectedDate = selectedDate.copyWith(
year: picked.year,
month: picked.month,
day: picked.day,
);
dateController.text = picked.toString().substring(0, 10);
});
}
},
icon: const Icon(FeatherIcons.calendar),
),
),
),
),
],
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
RichText(
text: TextSpan(
text: "${lang.S.of(context).totalDueAmount}: ",
style: _theme.textTheme.bodyMedium?.copyWith(
fontSize: 14,
color: DAppColors.kSecondary,
),
children: [
TextSpan(
text: widget.customerModel.due == null
? '$currency${0}'
: '$currency${widget.customerModel.due!}',
style: const TextStyle(color: Color(0xFFFF8C34)),
),
]),
)
],
),
const SizedBox(height: 10),
TextFormField(
keyboardType: TextInputType.name,
readOnly: true,
initialValue: widget.customerModel.name,
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: lang.S.of(context).customerName,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 24),
///_____Total______________________________
Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(5),
),
color: _theme.colorScheme.primaryContainer,
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withValues(alpha: 0.08),
spreadRadius: 0,
offset: const Offset(0, 4),
blurRadius: 24,
),
],
),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(
color: Color(0xffFEF0F1),
borderRadius: BorderRadius.only(
topRight: Radius.circular(5),
topLeft: Radius.circular(5),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
lang.S.of(context).totalAmount,
style: const TextStyle(fontSize: 16),
),
Text(
dueAmount.toStringAsFixed(2),
style: const TextStyle(fontSize: 16),
),
],
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
lang.S.of(context).paidAmount,
style: const TextStyle(fontSize: 16),
),
SizedBox(
width: context.width() / 4,
height: 30,
child: TextFormField(
controller: paidText,
// Make ReadOnly if multiple payments are selected to avoid conflict
readOnly: (paymentWidgetKey.currentState?.getPaymentEntries().length ?? 1) > 1,
textAlign: TextAlign.right,
decoration: const InputDecoration(
hintText: '0',
hintStyle: TextStyle(color: kNeutralColor),
border: UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
focusedBorder: UnderlineInputBorder(),
contentPadding: EdgeInsets.symmetric(horizontal: 0, vertical: 8),
),
keyboardType: TextInputType.number,
),
),
],
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
lang.S.of(context).dueAmount,
style: const TextStyle(fontSize: 16),
),
Text(
calculateDueAmount(total: paidAmount).toStringAsFixed(2),
style: const TextStyle(fontSize: 16),
),
],
),
),
],
),
),
const SizedBox(height: 10),
],
),
),
///__________Payment_Type_Widget_______________________________________
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
const Divider(height: 20),
MultiPaymentWidget(
key: paymentWidgetKey,
showWalletOption: true, // Configure as needed
showChequeOption: (widget.customerModel.type != 'Supplier'), // Configure as needed
totalAmountController: paidText,
onPaymentListChanged: () {},
),
const Divider(height: 20),
],
),
),
],
),
),
bottomNavigationBar: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16),
child: Row(
children: [
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
maximumSize: const Size(double.infinity, 48),
minimumSize: const Size(double.infinity, 48),
disabledBackgroundColor: _theme.colorScheme.primary.withValues(alpha: 0.15),
),
onPressed: () async {
Navigator.pop(context);
},
child: Text(
lang.S.of(context).cancel,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: _theme.textTheme.bodyMedium?.copyWith(
color: _theme.colorScheme.primary,
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
),
const SizedBox(width: 20),
Expanded(
child: ElevatedButton(
style: OutlinedButton.styleFrom(
maximumSize: const Size(double.infinity, 48),
minimumSize: const Size(double.infinity, 48),
disabledBackgroundColor: _theme.colorScheme.primary.withValues(alpha: 0.15),
),
onPressed: () async {
if (paidAmount > 0 && dueAmount > 0) {
// Get payments from widget
List<PaymentEntry> payments = paymentWidgetKey.currentState?.getPaymentEntries() ?? [];
if (payments.isEmpty) {
EasyLoading.showError(lang.S.of(context).noDueSelected); // Or "Please select payment"
} else {
EasyLoading.show();
// Serialize Payment List
List<Map<String, dynamic>> paymentData = payments.map((e) => e.toJson()).toList();
DueRepo repo = DueRepo();
DueCollection? dueData;
dueData = await repo.dueCollect(
ref: consumerRef,
context: context,
partyId: widget.customerModel.id ?? 0,
invoiceNumber: selectedInvoice?.invoiceNumber,
paymentDate: selectedDate.toIso8601String(),
payments: paymentData,
payDueAmount: paidAmount,
);
if (dueData != null) {
DueInvoiceDetails(
dueCollection: dueData,
personalInformationModel: data,
isFromDue: true,
).launch(context);
}
}
} else {
EasyLoading.showError(
lang.S.of(context).noDueSelected,
);
}
},
child: Text(
lang.S.of(context).save,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: _theme.textTheme.bodyMedium?.copyWith(
color: _theme.colorScheme.primaryContainer,
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
),
],
),
),
),
);
}, error: (e, stack) {
return Center(
child: Text(e.toString()),
);
}, loading: () {
return const Center(child: CircularProgressIndicator());
});
});
}
}