Inventory Management | Customer Management | Invoice Management Done | Commodity Rate Sync Done

This commit is contained in:
2026-08-20 20:35:41 +05:30
parent ba9a9fd8a4
commit 5f620022f3
101 changed files with 9091 additions and 240 deletions

View File

@@ -42,9 +42,18 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
void _showCreateWalletDialog() {
final ctrl = TextEditingController();
final amtCtrl = TextEditingController();
final creditLimitCtrl = TextEditingController();
final fixedAmountCtrl = TextEditingController();
final cycleDateCtrl = TextEditingController();
DateTime openingDate = DateTime.now();
String selectedNature = 'CASH';
String? selectedSubNature;
String? selectedPaymentCycle;
final natures = ['CASH', 'SAVINGS', 'INVESTMENTS', 'LOAN', 'LENDING', 'PAYABLES', 'INCOME', 'EXPENSE', 'RECEIVABLES'];
final subNatures = ['CREDIT_CARD', 'OD_LIMIT', 'LOAN_EMI', 'POLICY_PREMIUM', 'OTHER'];
final paymentCycles = ['DAILY', 'WEEKLY', 'MONTHLY', 'QUARTERLY', 'YEARLY'];
showDialog(
context: context,
builder: (ctx) => StatefulBuilder(
@@ -117,14 +126,121 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
),
items: natures.map((n) => DropdownMenuItem(value: n, child: Text(n))).toList(),
onChanged: (val) {
if (val != null) setStateDialog(() => selectedNature = val);
if (val != null) setStateDialog(() {
selectedNature = val;
if (val != 'PAYABLES') {
selectedSubNature = null;
selectedPaymentCycle = null;
creditLimitCtrl.clear();
fixedAmountCtrl.clear();
cycleDateCtrl.clear();
} else {
selectedSubNature = 'CREDIT_CARD';
selectedPaymentCycle = 'MONTHLY';
}
});
},
),
const SizedBox(height: 16),
if (selectedNature == 'PAYABLES') ...[
DropdownButtonFormField<String>(
value: selectedSubNature,
style: const TextStyle(fontWeight: FontWeight.w500, color: Colors.black87),
decoration: InputDecoration(
labelText: 'Sub-Nature',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: Color(0xFF6C63FF), width: 2)),
),
items: subNatures.map((n) => DropdownMenuItem(value: n, child: Text(n.replaceAll('_', ' ')))).toList(),
onChanged: (val) {
if (val != null) setStateDialog(() => selectedSubNature = val);
},
),
const SizedBox(height: 16),
if (selectedSubNature == 'CREDIT_CARD' || selectedSubNature == 'OD_LIMIT') ...[
TextField(
controller: creditLimitCtrl,
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: false),
style: const TextStyle(fontWeight: FontWeight.w500),
decoration: InputDecoration(
hintText: 'Credit Limit (Optional)',
prefixText: 'Rs. ',
prefixStyle: const TextStyle(color: Colors.black87, fontWeight: FontWeight.w500, fontSize: 16),
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: Color(0xFF6C63FF), width: 2)),
),
),
const SizedBox(height: 16),
],
if (selectedSubNature == 'LOAN_EMI' || selectedSubNature == 'POLICY_PREMIUM') ...[
TextField(
controller: fixedAmountCtrl,
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: false),
style: const TextStyle(fontWeight: FontWeight.w500),
decoration: InputDecoration(
hintText: 'Fixed Amount Due (Optional)',
prefixText: 'Rs. ',
prefixStyle: const TextStyle(color: Colors.black87, fontWeight: FontWeight.w500, fontSize: 16),
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: Color(0xFF6C63FF), width: 2)),
),
),
const SizedBox(height: 16),
],
Row(
children: [
Expanded(
flex: 2,
child: DropdownButtonFormField<String>(
value: selectedPaymentCycle,
style: const TextStyle(fontWeight: FontWeight.w500, color: Colors.black87),
decoration: InputDecoration(
labelText: 'Cycle',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: Color(0xFF6C63FF), width: 2)),
),
items: paymentCycles.map((n) => DropdownMenuItem(value: n, child: Text(n))).toList(),
onChanged: (val) {
if (val != null) setStateDialog(() => selectedPaymentCycle = val);
},
),
),
const SizedBox(width: 8),
Expanded(
flex: 1,
child: TextField(
controller: cycleDateCtrl,
keyboardType: TextInputType.number,
style: const TextStyle(fontWeight: FontWeight.w500),
decoration: InputDecoration(
labelText: 'Date (1-31)',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: Color(0xFF6C63FF), width: 2)),
),
),
),
],
),
const SizedBox(height: 16),
],
TextField(
controller: amtCtrl,
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: false),
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true),
textInputAction: TextInputAction.done,
style: const TextStyle(fontWeight: FontWeight.w500),
decoration: InputDecoration(
@@ -189,20 +305,30 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
onPressed: () async {
if (ctrl.text.isNotEmpty) {
final double amt = double.tryParse(amtCtrl.text) ?? 0.0;
final double? cl = double.tryParse(creditLimitCtrl.text);
final double? fa = double.tryParse(fixedAmountCtrl.text);
final int? cd = int.tryParse(cycleDateCtrl.text);
final newWallet = await ref.read(walletProvider.notifier).createWallet(
name: ctrl.text,
nature: selectedNature,
initialBalance: 0.0,
subNature: selectedNature == 'PAYABLES' ? selectedSubNature : null,
creditLimit: cl,
fixedAmount: fa,
paymentCycle: selectedNature == 'PAYABLES' ? selectedPaymentCycle : null,
cycleDate: cd,
);
if (amt > 0) {
if (amt != 0) {
final tx = Transaction(
id: 0,
type: 'INCOME',
amount: amt,
type: amt > 0 ? 'INCOME' : 'EXPENSE',
amount: amt.abs(),
date: openingDate,
description: 'Opening Balance',
toWalletId: newWallet.id,
fromWalletId: amt < 0 ? newWallet.id : null,
toWalletId: amt > 0 ? newWallet.id : null,
);
await ref.read(transactionProvider.notifier).addTransaction(tx);
}
@@ -335,7 +461,6 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
final walletsState = ref.watch(walletProvider);
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
bottom: false,
child: Column(
@@ -472,7 +597,16 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
onPressed: () {
final editCtrl = TextEditingController(text: w.name);
String editNature = w.nature ?? 'CASH';
String? editSubNature = w.subNature;
final editCreditLimitCtrl = TextEditingController(text: w.creditLimit?.toString() ?? '');
final editFixedAmountCtrl = TextEditingController(text: w.fixedAmount?.toString() ?? '');
final editCycleDateCtrl = TextEditingController(text: w.cycleDate?.toString() ?? '');
String? editPaymentCycle = w.paymentCycle;
final natures = ['CASH', 'SAVINGS', 'EXPENSE', 'INVESTMENTS', 'LOAN', 'LENDING', 'PAYABLES', 'RECEIVABLES', 'INCOME'];
final subNatures = ['CREDIT_CARD', 'OD_LIMIT', 'LOAN_EMI', 'POLICY_PREMIUM', 'OTHER'];
final paymentCycles = ['DAILY', 'WEEKLY', 'MONTHLY', 'QUARTERLY', 'YEARLY'];
showDialog(
context: context,
builder: (ctx) => StatefulBuilder(
@@ -480,9 +614,11 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(28)),
elevation: 0,
backgroundColor: Colors.transparent,
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(28)),
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(28)),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -510,10 +646,107 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
),
items: natures.map((n) => DropdownMenuItem(value: n, child: Text(n))).toList(),
onChanged: (val) {
if (val != null) setStateDialog(() => editNature = val);
if (val != null) setStateDialog(() {
editNature = val;
if (val != 'PAYABLES') {
editSubNature = null;
editPaymentCycle = null;
editCreditLimitCtrl.clear();
editFixedAmountCtrl.clear();
editCycleDateCtrl.clear();
} else if (editSubNature == null) {
editSubNature = 'CREDIT_CARD';
editPaymentCycle = 'MONTHLY';
}
});
},
),
const SizedBox(height: 24),
const SizedBox(height: 16),
if (editNature == 'PAYABLES') ...[
DropdownButtonFormField<String>(
value: editSubNature,
decoration: InputDecoration(
labelText: 'Sub-Nature',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
items: subNatures.map((n) => DropdownMenuItem(value: n, child: Text(n.replaceAll('_', ' ')))).toList(),
onChanged: (val) {
if (val != null) setStateDialog(() => editSubNature = val);
},
),
const SizedBox(height: 16),
if (editSubNature == 'CREDIT_CARD' || editSubNature == 'OD_LIMIT') ...[
TextField(
controller: editCreditLimitCtrl,
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: false),
decoration: InputDecoration(
hintText: 'Credit Limit (Optional)',
prefixText: 'Rs. ',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
const SizedBox(height: 16),
],
if (editSubNature == 'LOAN_EMI' || editSubNature == 'POLICY_PREMIUM') ...[
TextField(
controller: editFixedAmountCtrl,
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: false),
decoration: InputDecoration(
hintText: 'Fixed Amount Due (Optional)',
prefixText: 'Rs. ',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
const SizedBox(height: 16),
],
Row(
children: [
Expanded(
flex: 2,
child: DropdownButtonFormField<String>(
value: editPaymentCycle,
decoration: InputDecoration(
labelText: 'Cycle',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
items: paymentCycles.map((n) => DropdownMenuItem(value: n, child: Text(n))).toList(),
onChanged: (val) {
if (val != null) setStateDialog(() => editPaymentCycle = val);
},
),
),
const SizedBox(width: 8),
Expanded(
flex: 1,
child: TextField(
controller: editCycleDateCtrl,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'Date (1-31)',
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
),
],
),
const SizedBox(height: 16),
],
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
@@ -525,11 +758,20 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
ElevatedButton(
onPressed: () async {
if (editCtrl.text.isNotEmpty) {
final double? cl = double.tryParse(editCreditLimitCtrl.text);
final double? fa = double.tryParse(editFixedAmountCtrl.text);
final int? cd = int.tryParse(editCycleDateCtrl.text);
try {
await ref.read(walletProvider.notifier).editWallet(
w.id,
name: editCtrl.text.trim(),
nature: editNature,
subNature: editNature == 'PAYABLES' ? editSubNature : null,
creditLimit: cl,
fixedAmount: fa,
paymentCycle: editNature == 'PAYABLES' ? editPaymentCycle : null,
cycleDate: cd,
);
if (context.mounted) {
Navigator.pop(ctx);
@@ -549,6 +791,7 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
),
],
),
),
),
),
),