285 lines
9.6 KiB
Dart
285 lines
9.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_picker_plus/picker.dart';
|
|
import 'package:get_it/get_it.dart';
|
|
import 'package:okane/database/collections/account.dart';
|
|
import 'package:okane/database/collections/beneficiary.dart';
|
|
import 'package:okane/database/collections/recurrent.dart';
|
|
import 'package:okane/database/collections/template.dart';
|
|
import 'package:okane/database/database.dart';
|
|
import 'package:okane/ui/state/core.dart';
|
|
import 'package:okane/ui/transaction.dart';
|
|
import 'package:okane/ui/utils.dart';
|
|
import 'package:searchfield/searchfield.dart';
|
|
|
|
enum Period { days, weeks, months, years }
|
|
|
|
class AddRecurringTransactionTemplateWidget extends StatefulWidget {
|
|
final VoidCallback onAdd;
|
|
|
|
final Account activeAccountItem;
|
|
|
|
const AddRecurringTransactionTemplateWidget({
|
|
super.key,
|
|
required this.activeAccountItem,
|
|
required this.onAdd,
|
|
});
|
|
|
|
@override
|
|
State<AddRecurringTransactionTemplateWidget> createState() =>
|
|
_AddRecurringTransactionTemplateWidgetState();
|
|
}
|
|
|
|
class _AddRecurringTransactionTemplateWidgetState
|
|
extends State<AddRecurringTransactionTemplateWidget> {
|
|
final TextEditingController _beneficiaryTextController =
|
|
TextEditingController();
|
|
final TextEditingController _amountTextController = TextEditingController();
|
|
final TextEditingController _templateNameController = TextEditingController();
|
|
|
|
List<Beneficiary> beneficiaries = [];
|
|
|
|
SearchFieldListItem<Beneficiary>? _selectedBeneficiary;
|
|
|
|
TransactionDirection _selectedDirection = TransactionDirection.send;
|
|
|
|
Period _selectedPeriod = Period.months;
|
|
int _periodSize = 1;
|
|
|
|
String getBeneficiaryName(Beneficiary item) {
|
|
return switch (item.type) {
|
|
BeneficiaryType.account => "${item.name} (Account)",
|
|
BeneficiaryType.other => item.name,
|
|
};
|
|
}
|
|
|
|
Future<void> _submit(BuildContext context) async {
|
|
final beneficiaryName = _beneficiaryTextController.text;
|
|
if (_selectedBeneficiary == null && beneficiaryName.isEmpty) {
|
|
return;
|
|
}
|
|
if (_templateNameController.text.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
Beneficiary? beneficiary = _selectedBeneficiary?.item;
|
|
if (beneficiary == null ||
|
|
getBeneficiaryName(beneficiary) != beneficiaryName) {
|
|
// Add a new beneficiary, if none was selected
|
|
final result = await showDialog<bool>(
|
|
context: context,
|
|
builder:
|
|
(context) => AlertDialog(
|
|
title: const Text("Add Beneficiary"),
|
|
content: Text(
|
|
"The beneficiary '$beneficiaryName' does not exist. Do you want to add it?",
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
style: TextButton.styleFrom(
|
|
textStyle: Theme.of(context).textTheme.labelLarge,
|
|
),
|
|
child: const Text('Add'),
|
|
onPressed: () => Navigator.of(context).pop(true),
|
|
),
|
|
TextButton(
|
|
style: TextButton.styleFrom(
|
|
textStyle: Theme.of(context).textTheme.labelLarge,
|
|
),
|
|
child: const Text('Cancel'),
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (result == null || !result) {
|
|
return;
|
|
}
|
|
|
|
beneficiary =
|
|
Beneficiary()
|
|
..name = beneficiaryName
|
|
..type = BeneficiaryType.other;
|
|
await upsertBeneficiary(beneficiary);
|
|
}
|
|
|
|
final days = switch (_selectedPeriod) {
|
|
Period.days => _periodSize,
|
|
Period.weeks => _periodSize * 7,
|
|
Period.months => _periodSize * 31,
|
|
Period.years => _periodSize * 365,
|
|
};
|
|
final factor = switch (_selectedDirection) {
|
|
TransactionDirection.send => -1,
|
|
TransactionDirection.receive => 1,
|
|
};
|
|
final amount = factor * double.parse(_amountTextController.text).abs();
|
|
final template =
|
|
TransactionTemplate()
|
|
..name = _templateNameController.text
|
|
..beneficiary.value = beneficiary
|
|
..account.value = widget.activeAccountItem
|
|
..recurring = true
|
|
..amount = amount;
|
|
await upsertTransactionTemplate(template);
|
|
|
|
final transaction =
|
|
RecurringTransaction()
|
|
..lastExecution = null
|
|
..template.value = template
|
|
..account.value = widget.activeAccountItem
|
|
..days = days;
|
|
await upsertRecurringTransaction(transaction);
|
|
|
|
_periodSize = 1;
|
|
_selectedPeriod = Period.weeks;
|
|
_amountTextController.text = "";
|
|
_templateNameController.text = "";
|
|
widget.onAdd();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
|
child: ListView(
|
|
shrinkWrap: true,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: TextField(
|
|
controller: _templateNameController,
|
|
decoration: InputDecoration(label: Text("Template name")),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: SearchField<Beneficiary>(
|
|
suggestions:
|
|
beneficiaries
|
|
.where((el) {
|
|
final bloc = GetIt.I.get<CoreCubit>();
|
|
if (el.type == BeneficiaryType.account) {
|
|
return el.account.value?.id != bloc.activeAccount?.id;
|
|
}
|
|
return true;
|
|
})
|
|
.map((el) {
|
|
return SearchFieldListItem(
|
|
getBeneficiaryName(el),
|
|
item: el,
|
|
);
|
|
})
|
|
.toList(),
|
|
hint: "Beneficiary",
|
|
controller: _beneficiaryTextController,
|
|
selectedValue: _selectedBeneficiary,
|
|
onSuggestionTap: (beneficiary) {
|
|
setState(() => _selectedBeneficiary = beneficiary);
|
|
},
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: TextField(
|
|
controller: _amountTextController,
|
|
keyboardType: TextInputType.numberWithOptions(
|
|
signed: false,
|
|
decimal: false,
|
|
),
|
|
decoration: InputDecoration(hintText: "Amount"),
|
|
),
|
|
),
|
|
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: SegmentedButton<TransactionDirection>(
|
|
segments: [
|
|
ButtonSegment(
|
|
value: TransactionDirection.send,
|
|
label: Text("Send"),
|
|
icon: Icon(Icons.remove),
|
|
),
|
|
ButtonSegment(
|
|
value: TransactionDirection.receive,
|
|
label: Text("Receive"),
|
|
icon: Icon(Icons.add),
|
|
),
|
|
],
|
|
selected: <TransactionDirection>{_selectedDirection},
|
|
multiSelectionEnabled: false,
|
|
onSelectionChanged: (selection) {
|
|
setState(() => _selectedDirection = selection.first);
|
|
},
|
|
),
|
|
),
|
|
|
|
Padding(
|
|
padding: EdgeInsets.only(left: 16, right: 16, top: 16),
|
|
child: SegmentedButton<Period>(
|
|
segments: [
|
|
ButtonSegment(value: Period.days, label: Text("Days")),
|
|
ButtonSegment(value: Period.weeks, label: Text("Weeks")),
|
|
ButtonSegment(value: Period.months, label: Text("Months")),
|
|
ButtonSegment(value: Period.years, label: Text("Years")),
|
|
],
|
|
selected: <Period>{_selectedPeriod},
|
|
multiSelectionEnabled: false,
|
|
onSelectionChanged: (selection) {
|
|
setState(() => _selectedPeriod = selection.first);
|
|
},
|
|
),
|
|
),
|
|
Text.rich(
|
|
TextSpan(
|
|
text: "Repeat every ",
|
|
children: [
|
|
WidgetSpan(
|
|
child: TextButton(
|
|
onPressed: () {
|
|
Picker(
|
|
adapter: NumberPickerAdapter(
|
|
data: [
|
|
NumberPickerColumn(
|
|
begin: 1,
|
|
end: 999,
|
|
initValue: _periodSize,
|
|
),
|
|
],
|
|
),
|
|
hideHeader: true,
|
|
selectedTextStyle: TextStyle(color: Colors.blue),
|
|
onConfirm: (Picker picker, List value) {
|
|
setState(() {
|
|
_periodSize = (value.first as int) + 1;
|
|
});
|
|
},
|
|
).showDialog(context);
|
|
},
|
|
child: Text(_periodSize.toString()),
|
|
),
|
|
),
|
|
TextSpan(
|
|
text: switch (_selectedPeriod) {
|
|
Period.days => " days",
|
|
Period.weeks => " weeks",
|
|
Period.months => " months",
|
|
Period.years => " years",
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
Align(
|
|
alignment: Alignment.centerRight,
|
|
child: OutlinedButton(
|
|
onPressed: () => _submit(context),
|
|
child: Text("Add"),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|