first commit
This commit is contained in:
466
lib/Screens/vat_&_tax/add_group_tax.dart
Normal file
466
lib/Screens/vat_&_tax/add_group_tax.dart
Normal file
@@ -0,0 +1,466 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/provider/text_repo.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/repo/tax_repo.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import '../../http_client/custome_http_client.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
import 'model/vat_model.dart';
|
||||
|
||||
class AddGroupTax extends ConsumerStatefulWidget {
|
||||
const AddGroupTax({
|
||||
super.key,
|
||||
this.taxModel,
|
||||
});
|
||||
|
||||
final VatModel? taxModel;
|
||||
|
||||
@override
|
||||
AddTaxGroupState createState() => AddTaxGroupState();
|
||||
}
|
||||
|
||||
class AddTaxGroupState extends ConsumerState<AddGroupTax> {
|
||||
List<VatModel> subTaxList = [];
|
||||
|
||||
TextEditingController nameController = TextEditingController();
|
||||
bool status = true;
|
||||
|
||||
final GlobalKey<FormState> _fromKey = GlobalKey<FormState>();
|
||||
|
||||
void _saveTax({required BuildContext context, required WidgetRef ref}) async {}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.taxModel?.subTax != null) {
|
||||
Future.microtask(() async {
|
||||
final data = await ref.read(singleTaxProvider.future);
|
||||
|
||||
List<VatModel> matchingItems = [];
|
||||
|
||||
for (var element in widget.taxModel!.subTax!) {
|
||||
try {
|
||||
VatModel matchingItem = data.firstWhere(
|
||||
(item) => element.id == item.id,
|
||||
orElse: () => VatModel(),
|
||||
);
|
||||
|
||||
if (matchingItem.id != null) {
|
||||
matchingItems.add(matchingItem);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
subTaxList = matchingItems;
|
||||
});
|
||||
});
|
||||
nameController.text = widget.taxModel?.name ?? '';
|
||||
status = widget.taxModel?.status ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = lang.S.of(context);
|
||||
final permissionService = PermissionService(ref);
|
||||
return Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
widget.taxModel == null ? _lang.addTaxGroup : _lang.editTaxGroup,
|
||||
// style: GoogleFonts.poppins(
|
||||
// color: Colors.white,
|
||||
// ),
|
||||
),
|
||||
// iconTheme: const IconThemeData(color: Colors.white),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0.0,
|
||||
),
|
||||
body: Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(30),
|
||||
topLeft: Radius.circular(30),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
//___________________________________Tax Rates______________________________
|
||||
Text('${widget.taxModel == null ? _lang.add : _lang.edit} ${_lang.taxWithSingleMultipleTaxType}',
|
||||
style: const TextStyle(color: kTitleColor, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10.0),
|
||||
Text('${lang.S.of(context).name}*', style: const TextStyle(color: kTitleColor)),
|
||||
const SizedBox(height: 8.0),
|
||||
Form(
|
||||
key: _fromKey,
|
||||
child: TextFormField(
|
||||
controller: nameController,
|
||||
keyboardType: TextInputType.text,
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return 'Tax name is required';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.only(left: 8, right: 8.0),
|
||||
border: const OutlineInputBorder(),
|
||||
// hintText: 'Enter Name',
|
||||
hintText: lang.S.of(context).enterName,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
Text('${_lang.subTaxes}*', style: TextStyle(color: kTitleColor)),
|
||||
const SizedBox(height: 8.0),
|
||||
Consumer(builder: (context, ref, __) {
|
||||
final taxes = ref.watch(singleTaxProvider);
|
||||
return taxes.when(
|
||||
data: (taxes) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
subTaxList = await getTaxesModalSheet(mainContext: context, ref: ref, oldList: subTaxList, taxList: taxes);
|
||||
setState(() {
|
||||
subTaxList;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0), color: Colors.transparent, border: Border.all(color: kBorderColorTextField)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
subTaxList.isNotEmpty
|
||||
? Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Wrap(
|
||||
children: List.generate(
|
||||
subTaxList.length,
|
||||
(index) {
|
||||
final category = subTaxList[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 5.0),
|
||||
child: Container(
|
||||
height: 30,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0), color: kMainColor),
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
subTaxList.removeAt(index);
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: kWhite,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
category.name ?? '',
|
||||
style: const TextStyle(color: kWhite),
|
||||
),
|
||||
const SizedBox(width: 8)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(_lang.noSubTaxSelected, style: TextStyle(color: kTitleColor)),
|
||||
|
||||
//___________________________________________showModalBottomSheet______________________
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(11.0),
|
||||
child: Icon(
|
||||
Icons.keyboard_arrow_down_rounded,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
error: (error, stackTrace) {
|
||||
return Text(error.toString());
|
||||
},
|
||||
loading: () => Container(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0), color: Colors.transparent, border: Border.all(color: kBorderColorTextField)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(_lang.noSubTaxSelected, style: TextStyle(color: kTitleColor)),
|
||||
|
||||
//___________________________________________showModalBottomSheet______________________
|
||||
Padding(
|
||||
padding: EdgeInsets.all(11.0),
|
||||
child: Icon(
|
||||
Icons.keyboard_arrow_down_rounded,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
// loading: () => Skeletonizer(
|
||||
// enabled: true,
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.only(left: 10),
|
||||
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0), color: Colors.transparent, border: Border.all(color: kBorderColorTextField)),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Text(_lang.noSubTaxSelected, style: TextStyle(color: kTitleColor)),
|
||||
//
|
||||
// //___________________________________________showModalBottomSheet______________________
|
||||
// Padding(
|
||||
// padding: EdgeInsets.all(11.0),
|
||||
// child: Icon(
|
||||
// Icons.keyboard_arrow_down_rounded,
|
||||
// color: kGreyTextColor,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ));
|
||||
}),
|
||||
const SizedBox(height: 20.0),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
_lang.status,
|
||||
style: TextStyle(color: kTitleColor),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Switch(
|
||||
value: status,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
status = value;
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
//___________________________________________save_button______________________
|
||||
const Spacer(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: SizedBox(
|
||||
height: 45.0,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
backgroundColor: kMainColor,
|
||||
elevation: 1.0,
|
||||
foregroundColor: kGreyTextColor.withValues(alpha: 0.1),
|
||||
shadowColor: kMainColor,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: const TextStyle(color: Colors.white, fontFamily: 'Display', fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (widget.taxModel == null) {
|
||||
if (!permissionService.hasPermission(Permit.vatsCreate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text('You do not have permission to create tax.'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!permissionService.hasPermission(Permit.vatsUpdate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text('You do not have permission to update tax.'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_fromKey.currentState!.validate()) {
|
||||
if (subTaxList.isNotEmpty) {
|
||||
EasyLoading.show();
|
||||
TaxRepo repo = TaxRepo();
|
||||
List<num> ids = [];
|
||||
for (var element in subTaxList) {
|
||||
ids.add(element.id!);
|
||||
}
|
||||
if (widget.taxModel != null) {
|
||||
await repo.updateGroupTax(id: widget.taxModel!.id!, ref: ref, context: context, taxName: nameController.text, taxIds: ids, status: status);
|
||||
} else {
|
||||
await repo.createGroupTax(ref: ref, context: context, taxName: nameController.text, taxIds: ids, status: status);
|
||||
}
|
||||
EasyLoading.dismiss();
|
||||
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
EasyLoading.showError('Please select taxes');
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).save,
|
||||
style: const TextStyle(color: kWhite, fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<VatModel>> getTaxesModalSheet({
|
||||
required BuildContext mainContext,
|
||||
required WidgetRef ref,
|
||||
required List<VatModel> oldList,
|
||||
required List<VatModel> taxList,
|
||||
}) async {
|
||||
List<VatModel> subTaxList = [...oldList];
|
||||
|
||||
bool? isDone = await showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
backgroundColor: Colors.white,
|
||||
context: mainContext,
|
||||
builder: (BuildContext context) {
|
||||
final _lang = lang.S.of(context);
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setNewState) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20.0, 13.0, 0.0, 0.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
_lang.subTaxList,
|
||||
style: TextStyle(color: kTitleColor, fontWeight: FontWeight.w600, fontSize: 20),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: const Icon(
|
||||
Icons.close_rounded,
|
||||
size: 21,
|
||||
color: kTitleColor,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(color: kBorderColorTextField),
|
||||
// const SizedBox(height: 5),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
|
||||
itemCount: taxList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final category = taxList[index];
|
||||
return Column(
|
||||
children: [
|
||||
CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
checkboxShape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
),
|
||||
checkColor: Colors.white,
|
||||
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
),
|
||||
// fillColor: WidgetStateProperty.all(
|
||||
// subTaxList.contains(category) ? kMainColor : Colors.transparent,
|
||||
// ),
|
||||
fillColor: WidgetStatePropertyAll(subTaxList.contains(category) ? kMainColor : kBackgroundColor),
|
||||
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
|
||||
side: const BorderSide(color: kBorderColorTextField),
|
||||
title: Text(category.name ?? '', style: const TextStyle(color: kTitleColor, overflow: TextOverflow.ellipsis)),
|
||||
subtitle: Text('${_lang.taxPercent}: ${category.rate}%', style: const TextStyle(color: kGreyTextColor)),
|
||||
value: subTaxList.contains(category),
|
||||
onChanged: (isChecked) {
|
||||
setNewState(() {
|
||||
if (isChecked!) {
|
||||
if (!subTaxList.contains(category)) {
|
||||
subTaxList.add(category); // Add only the TaxModel instance
|
||||
}
|
||||
} else {
|
||||
subTaxList.remove(category);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
color: kBorderColorTextField,
|
||||
height: 0.0,
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: SizedBox(
|
||||
height: 45.0,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
backgroundColor: kMainColor,
|
||||
elevation: 1.0,
|
||||
foregroundColor: kGreyTextColor.withValues(alpha: 0.1),
|
||||
shadowColor: kMainColor,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: const TextStyle(color: Colors.white, fontFamily: 'Display', fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: Text(_lang.done, style: TextStyle(color: kWhite, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
return (isDone ?? false) ? subTaxList : oldList;
|
||||
}
|
||||
248
lib/Screens/vat_&_tax/creating_single_tax.dart
Normal file
248
lib/Screens/vat_&_tax/creating_single_tax.dart
Normal file
@@ -0,0 +1,248 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/model/vat_model.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/repo/tax_repo.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../http_client/custome_http_client.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
|
||||
class CreateSingleTax extends ConsumerStatefulWidget {
|
||||
const CreateSingleTax({super.key, this.taxModel});
|
||||
|
||||
final VatModel? taxModel;
|
||||
|
||||
@override
|
||||
ConsumerState<CreateSingleTax> createState() => _CreateSingleTaxState();
|
||||
}
|
||||
|
||||
class _CreateSingleTaxState extends ConsumerState<CreateSingleTax> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late TextEditingController taxNameController;
|
||||
late TextEditingController taxRateController;
|
||||
|
||||
bool status = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
taxNameController = TextEditingController(text: widget.taxModel?.name ?? '');
|
||||
taxRateController = TextEditingController(
|
||||
text: widget.taxModel?.rate != null ? widget.taxModel!.rate.toString() : '',
|
||||
);
|
||||
status = widget.taxModel?.status ?? true;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
taxNameController.dispose();
|
||||
taxRateController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _saveTax({required BuildContext context, required WidgetRef ref}) async {}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = lang.S.of(context);
|
||||
final permissionService = PermissionService(ref);
|
||||
return Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
widget.taxModel == null ? _lang.addTax : _lang.editTax,
|
||||
),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
elevation: 0.0,
|
||||
),
|
||||
body: Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(30),
|
||||
topLeft: Radius.circular(30),
|
||||
),
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.taxModel == null ? _lang.addNewTax : _lang.editTax,
|
||||
// 'Add New Tax',
|
||||
style: const TextStyle(color: kTitleColor, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
// Tax Name Field
|
||||
Text(
|
||||
'${lang.S.of(context).name}*',
|
||||
style: const TextStyle(color: kTitleColor),
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
TextFormField(
|
||||
controller: taxNameController,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: lang.S.of(context).enterName,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return 'Tax name is required';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
// Tax Rate Field
|
||||
Text(
|
||||
'${_lang.taxRates}*',
|
||||
style: TextStyle(color: kTitleColor),
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
TextFormField(
|
||||
controller: taxRateController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
border: OutlineInputBorder(),
|
||||
hintText: _lang.enterTaxRates,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return 'Tax rate is required';
|
||||
}
|
||||
if (double.tryParse(value.trim()) == null) {
|
||||
return 'Enter a valid number';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
_lang.status,
|
||||
style: TextStyle(color: kTitleColor),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Switch(
|
||||
value: status,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
status = value;
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
// Save Button
|
||||
Consumer(builder: (context1, ref, __) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: SizedBox(
|
||||
height: 45.0,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
backgroundColor: kMainColor,
|
||||
elevation: 1.0,
|
||||
shadowColor: kMainColor,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (widget.taxModel == null) {
|
||||
if (!permissionService.hasPermission(Permit.vatsCreate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text('You do not have permission to create tax.'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!permissionService.hasPermission(Permit.vatsUpdate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text('You do not have permission to update tax.'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
EasyLoading.show();
|
||||
|
||||
TaxRepo repo = TaxRepo();
|
||||
|
||||
final taxRate = num.tryParse(taxRateController.text) ?? 0;
|
||||
final taxName = taxNameController.text;
|
||||
|
||||
try {
|
||||
if (widget.taxModel == null) {
|
||||
await repo.createSingleTax(
|
||||
ref: ref,
|
||||
context: context,
|
||||
taxRate: taxRate,
|
||||
taxName: taxName,
|
||||
status: status,
|
||||
);
|
||||
} else {
|
||||
await repo.updateSingleTax(
|
||||
ref: ref,
|
||||
context: context,
|
||||
rate: taxRate,
|
||||
name: taxName,
|
||||
id: widget.taxModel!.id!,
|
||||
status: status,
|
||||
);
|
||||
}
|
||||
|
||||
EasyLoading.dismiss();
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text('An error occurred: $e'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
_lang.save,
|
||||
style: TextStyle(
|
||||
color: kWhite,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
34
lib/Screens/vat_&_tax/model/group_text_model.dart
Normal file
34
lib/Screens/vat_&_tax/model/group_text_model.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/model/vat_model.dart';
|
||||
|
||||
class GroupTaxModel {
|
||||
late String name;
|
||||
late num taxRate;
|
||||
late String id;
|
||||
List<VatModel>? subTaxes;
|
||||
|
||||
GroupTaxModel({
|
||||
required this.name,
|
||||
required this.taxRate,
|
||||
required this.id,
|
||||
required this.subTaxes,
|
||||
});
|
||||
|
||||
GroupTaxModel.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
taxRate = json['rate'];
|
||||
id = json['id'];
|
||||
if (json['subTax'] != null) {
|
||||
subTaxes = <VatModel>[];
|
||||
json['subTax'].forEach((v) {
|
||||
subTaxes!.add(VatModel.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'name': name,
|
||||
'rate': taxRate,
|
||||
'id': id,
|
||||
'subTax': subTaxes?.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
78
lib/Screens/vat_&_tax/model/vat_model.dart
Normal file
78
lib/Screens/vat_&_tax/model/vat_model.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
class VatModel {
|
||||
VatModel({
|
||||
this.id,
|
||||
this.name,
|
||||
this.businessId,
|
||||
this.rate,
|
||||
this.subTax,
|
||||
this.status,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
});
|
||||
|
||||
VatModel.fromJson(dynamic json) {
|
||||
id = json['id'];
|
||||
name = json['name'];
|
||||
businessId = json['business_id'];
|
||||
rate = json['rate'];
|
||||
if (json['sub_vat'] != null) {
|
||||
subTax = [];
|
||||
json['sub_vat'].forEach((v) {
|
||||
subTax?.add(SubVat.fromJson(v));
|
||||
});
|
||||
}
|
||||
status = json['status'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
}
|
||||
|
||||
num? id;
|
||||
String? name;
|
||||
num? businessId;
|
||||
num? rate;
|
||||
List<SubVat>? subTax;
|
||||
bool? status;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = id;
|
||||
map['name'] = name;
|
||||
map['business_id'] = businessId;
|
||||
map['rate'] = rate;
|
||||
if (subTax != null) {
|
||||
map['sub_vat'] = subTax?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['status'] = status;
|
||||
map['created_at'] = createdAt;
|
||||
map['updated_at'] = updatedAt;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class SubVat {
|
||||
SubVat({
|
||||
this.id,
|
||||
this.name,
|
||||
this.rate,
|
||||
});
|
||||
|
||||
SubVat.fromJson(dynamic json) {
|
||||
id = json['id'];
|
||||
name = json['name'];
|
||||
rate = json['rate'];
|
||||
}
|
||||
|
||||
num? id;
|
||||
String? name;
|
||||
num? rate;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = id;
|
||||
map['name'] = name;
|
||||
map['rate'] = rate;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
11
lib/Screens/vat_&_tax/provider/text_repo.dart
Normal file
11
lib/Screens/vat_&_tax/provider/text_repo.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
//_____________________________________________Tax_provider_____________________
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../model/vat_model.dart';
|
||||
import '../repo/tax_repo.dart';
|
||||
|
||||
TaxRepo taxRepo = TaxRepo();
|
||||
final taxProvider = FutureProvider<List<VatModel>>((ref) => taxRepo.fetchAllTaxes(taxType: ''));
|
||||
|
||||
//_____________________________________________Group_Tax_provider_____________________
|
||||
final singleTaxProvider = FutureProvider.autoDispose<List<VatModel>>((ref) => taxRepo.fetchAllTaxes(taxType: 'single'));
|
||||
238
lib/Screens/vat_&_tax/repo/tax_repo.dart
Normal file
238
lib/Screens/vat_&_tax/repo/tax_repo.dart
Normal file
@@ -0,0 +1,238 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../Const/api_config.dart';
|
||||
import '../../../Repository/constant_functions.dart';
|
||||
import '../../../http_client/custome_http_client.dart';
|
||||
import '../../../http_client/customer_http_client_get.dart';
|
||||
import '../model/vat_model.dart';
|
||||
import '../provider/text_repo.dart';
|
||||
|
||||
class TaxRepo {
|
||||
Future<List<VatModel>> fetchAllTaxes({String? taxType}) async {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final uri = Uri.parse('${APIConfig.url}/vats?type=$taxType');
|
||||
|
||||
final response = await clientGet.get(url: uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final parsedData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
|
||||
final partyList = parsedData['data'] as List<dynamic>;
|
||||
return partyList.map((category) => VatModel.fromJson(category)).toList();
|
||||
// Parse into Party objects
|
||||
} else {
|
||||
throw Exception('Failed to fetch tax list');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createSingleTax({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required num taxRate,
|
||||
required String taxName,
|
||||
required bool status,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/vats');
|
||||
final requestBody = jsonEncode({
|
||||
'name': taxName,
|
||||
'rate': taxRate,
|
||||
});
|
||||
|
||||
try {
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
var responseData = await customHttpClient.post(
|
||||
url: uri,
|
||||
addContentTypeInHeader: true,
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
final parsedData = jsonDecode(responseData.body);
|
||||
|
||||
EasyLoading.dismiss();
|
||||
if (responseData.statusCode == 200) {
|
||||
ref.refresh(taxProvider);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Tax creation failed: ${parsedData}')));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
// Handle unexpected errors gracefully
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('An error occurred: $error')));
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createGroupTax({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required String taxName,
|
||||
required List<num> taxIds,
|
||||
required bool status,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/vats');
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
|
||||
var request = http.MultipartRequest('POST', uri)
|
||||
..headers['Accept'] = 'application/json'
|
||||
..headers['Authorization'] = await getAuthToken();
|
||||
request.fields.addAll({
|
||||
'name': taxName,
|
||||
});
|
||||
|
||||
if (taxIds.isNotEmpty) {
|
||||
int index = 0;
|
||||
for (var element in taxIds) {
|
||||
request.fields['vat_ids[$index]'] = element.toString();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await customHttpClient.uploadFile(
|
||||
url: uri,
|
||||
fields: request.fields,
|
||||
);
|
||||
// final response = await request.send();
|
||||
final responseData = await response.stream.bytesToString();
|
||||
final parsedData = jsonDecode(responseData);
|
||||
|
||||
EasyLoading.dismiss();
|
||||
print(response.statusCode);
|
||||
print(responseData);
|
||||
if (response.statusCode == 200) {
|
||||
print('45235');
|
||||
ref.refresh(taxProvider);
|
||||
} else if (response.statusCode == 403) {
|
||||
throw Exception('Failed to update tax');
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Tax creation failed: ${parsedData['message']}')));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
// Handle unexpected errors gracefully
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('An error occurred: $error')));
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
|
||||
///________Update_Single_Tax__________________________________________
|
||||
Future<void> updateSingleTax({
|
||||
required num id,
|
||||
required String name,
|
||||
required num rate,
|
||||
required bool status,
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/vats/$id');
|
||||
final requestBody = jsonEncode({
|
||||
'rate': rate,
|
||||
'name': name,
|
||||
'status': status,
|
||||
'_method': 'put',
|
||||
});
|
||||
|
||||
try {
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
|
||||
final response = await customHttpClient.post(
|
||||
url: uri,
|
||||
addContentTypeInHeader: true,
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
ref.refresh(taxProvider);
|
||||
} else {
|
||||
throw Exception('Failed to update tax. Status Code: ${response.statusCode} - ${response.body}');
|
||||
}
|
||||
} catch (error) {
|
||||
print('Error updating income: $error');
|
||||
throw Exception('Error updating income: $error');
|
||||
} finally {
|
||||
EasyLoading.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateGroupTax({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required num id,
|
||||
required String taxName,
|
||||
required List<num> taxIds,
|
||||
required bool status,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/vats/$id');
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
|
||||
var request = http.MultipartRequest('POST', uri)
|
||||
..headers['Accept'] = 'application/json'
|
||||
..headers['Authorization'] = await getAuthToken();
|
||||
request.fields.addAll({
|
||||
'name': taxName,
|
||||
'status': status ? '1' : "0",
|
||||
'_method': 'put',
|
||||
});
|
||||
|
||||
if (taxIds.isNotEmpty) {
|
||||
int index = 0;
|
||||
for (var element in taxIds) {
|
||||
request.fields['vat_ids[$index]'] = element.toString();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await customHttpClient.uploadFile(
|
||||
url: uri,
|
||||
fields: request.fields,
|
||||
);
|
||||
// final response = await request.send();
|
||||
final responseData = await response.stream.bytesToString();
|
||||
final parsedData = jsonDecode(responseData);
|
||||
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ref.refresh(taxProvider);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Tax creation failed: ${parsedData['message']}')));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
// Handle unexpected errors gracefully
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('An error occurred: $error')));
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
|
||||
///________Delete_Tax______________________________________________________
|
||||
Future<bool> deleteTax({required String id, required BuildContext context, required WidgetRef ref}) async {
|
||||
try {
|
||||
final token = await getAuthToken();
|
||||
if (token.isEmpty) {
|
||||
throw Exception('Authentication token is missing or empty');
|
||||
}
|
||||
|
||||
final url = Uri.parse('${APIConfig.url}/vats/$id');
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(ref: ref, context: context, client: http.Client());
|
||||
final response = await customHttpClient.delete(url: url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
print('Error deleting tax: ${response.statusCode} - ${response.body}');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
print('Error during delete operation: $error');
|
||||
return false;
|
||||
} finally {
|
||||
EasyLoading.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
567
lib/Screens/vat_&_tax/tax_report.dart
Normal file
567
lib/Screens/vat_&_tax/tax_report.dart
Normal file
@@ -0,0 +1,567 @@
|
||||
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/Provider/profile_provider.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/add_group_tax.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/creating_single_tax.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/provider/text_repo.dart';
|
||||
import 'package:mobile_pos/Screens/vat_&_tax/repo/tax_repo.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../http_client/custome_http_client.dart';
|
||||
import '../../widgets/empty_widget/_empty_widget.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
import '../hrm/widgets/deleteing_alart_dialog.dart';
|
||||
import 'model/vat_model.dart';
|
||||
|
||||
class TaxReport extends ConsumerStatefulWidget {
|
||||
const TaxReport({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<TaxReport> createState() => _TaxReportState();
|
||||
}
|
||||
|
||||
class _TaxReportState extends ConsumerState<TaxReport> {
|
||||
bool _isRefreshing = false; // Prevents multiple refresh calls
|
||||
|
||||
Future<void> refreshData(WidgetRef ref) async {
|
||||
if (_isRefreshing) return; // Prevent duplicate refresh calls
|
||||
_isRefreshing = true;
|
||||
|
||||
ref.refresh(taxProvider);
|
||||
|
||||
await Future.delayed(const Duration(seconds: 1)); // Optional delay
|
||||
_isRefreshing = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = lang.S.of(context);
|
||||
final taxes = ref.watch(taxProvider);
|
||||
final businessProviderData = ref.watch(businessInfoProvider);
|
||||
ref.watch(getExpireDateProvider(ref));
|
||||
final permissionService = PermissionService(ref);
|
||||
return businessProviderData.when(data: (details) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
surfaceTintColor: Colors.white,
|
||||
title: Text(
|
||||
_lang.taxRates,
|
||||
),
|
||||
centerTitle: true,
|
||||
backgroundColor: kWhite,
|
||||
elevation: 0.0,
|
||||
),
|
||||
body: taxes.when(
|
||||
data: (data) {
|
||||
List<VatModel> singleTaxes = [];
|
||||
List<VatModel> groupTaxes = [];
|
||||
for (var element in data) {
|
||||
if (element.subTax == null) {
|
||||
singleTaxes.add(element);
|
||||
} else {
|
||||
groupTaxes.add(element);
|
||||
}
|
||||
}
|
||||
if (!permissionService.hasPermission(Permit.vatsRead.value)) {
|
||||
return Center(child: PermitDenyWidget());
|
||||
}
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => refreshData(ref),
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
//___________________________________Tax Rates______________________________
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
_lang.taxRatesMangeYourTaxRates,
|
||||
),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
minimumSize: Size(60, 30),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
backgroundColor: kSuccessColor,
|
||||
elevation: 1.0,
|
||||
foregroundColor: kGreyTextColor.withValues(alpha: 0.1),
|
||||
shadowColor: kMainColor,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: const TextStyle(color: Colors.white, fontFamily: 'Display', fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const CreateSingleTax(),
|
||||
),
|
||||
);
|
||||
},
|
||||
label: Text(
|
||||
_lang.add,
|
||||
style: TextStyle(color: kWhite, fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
icon: const Icon(
|
||||
FeatherIcons.plus,
|
||||
size: 15,
|
||||
color: kWhite,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStateColor.resolveWith((states) => Colors.white),
|
||||
border: TableBorder.all(borderRadius: BorderRadius.circular(2.0), color: kBorderColorTextField),
|
||||
dividerThickness: 1.0,
|
||||
sortAscending: true,
|
||||
showCheckboxColumn: false,
|
||||
horizontalMargin: 5.0,
|
||||
columnSpacing: 10,
|
||||
dataRowMinHeight: 45,
|
||||
showBottomBorder: true,
|
||||
checkboxHorizontalMargin: 0.0,
|
||||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
label: Text(
|
||||
lang.S.of(context).name,
|
||||
// 'Name',
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'${_lang.taxRates} %',
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
_lang.status,
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
headingRowAlignment: MainAxisAlignment.center,
|
||||
label: Text(
|
||||
_lang.actions,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
rows: List.generate(
|
||||
singleTaxes.length,
|
||||
(index) => DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * .30,
|
||||
child: Text(
|
||||
'${singleTaxes[index].name}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(Center(
|
||||
child: Text(
|
||||
'${singleTaxes[index].rate.toString()}%',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
)),
|
||||
DataCell(Center(
|
||||
child: Text(
|
||||
(singleTaxes[index].status ?? false) ? _lang.active : _lang.disable,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
)),
|
||||
DataCell(
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(50, 25),
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
backgroundColor: kSuccessColor,
|
||||
elevation: 1.0,
|
||||
foregroundColor: kGreyTextColor.withValues(alpha: 0.1),
|
||||
shadowColor: kMainColor,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: const TextStyle(color: Colors.white, fontFamily: 'Display', fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CreateSingleTax(taxModel: singleTaxes[index]),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
FeatherIcons.edit,
|
||||
size: 15,
|
||||
color: kWhite,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
lang.S.of(context).edit,
|
||||
//'Edit',
|
||||
style: const TextStyle(color: kWhite, fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(50, 25),
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
elevation: 1.0,
|
||||
foregroundColor: Colors.white.withValues(alpha: 0.1),
|
||||
shadowColor: Colors.red,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (!permissionService.hasPermission(Permit.vatsDelete.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text('You do not have permission to delete tax.'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
bool result = await showDeleteConfirmationDialog(context: context, itemName: 'vat_&_tax');
|
||||
if (result) {
|
||||
EasyLoading.show(status: _lang.deleting);
|
||||
final repo = TaxRepo();
|
||||
try {
|
||||
final result = await repo.deleteTax(id: singleTaxes[index].id.toString(), ref: ref, context: context);
|
||||
if (result) {
|
||||
ref.refresh(taxProvider);
|
||||
EasyLoading.showSuccess(_lang.deletedSuccessFully);
|
||||
} else {
|
||||
EasyLoading.showError(_lang.failedToDeleteTheTax);
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.showError('${_lang.errorDeletingTax}: $e');
|
||||
} finally {
|
||||
EasyLoading.dismiss();
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.delete_outline,
|
||||
size: 17,
|
||||
color: kWhite,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(lang.S.of(context).delete, style: const TextStyle(color: kWhite, fontSize: 12, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
color: WidgetStateColor.resolveWith(
|
||||
(Set<WidgetState> states) {
|
||||
// Use index to determine whether the row is even or odd
|
||||
return index % 2 == 0 ? Colors.grey.shade100 : Colors.white;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
//___________________________________Tax Group______________________________
|
||||
const SizedBox(height: 40.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(_lang.taxGroup, style: TextStyle(color: kTitleColor, fontWeight: FontWeight.bold)),
|
||||
Text('(${_lang.combinationOfTheMultipleTaxes})', style: TextStyle(color: kGreyTextColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
minimumSize: Size(60, 30),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
backgroundColor: kSuccessColor,
|
||||
elevation: 1.0,
|
||||
foregroundColor: kGreyTextColor.withValues(alpha: 0.1),
|
||||
shadowColor: kMainColor,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: const TextStyle(color: Colors.white, fontFamily: 'Display', fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const AddGroupTax()),
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
FeatherIcons.plus,
|
||||
size: 15,
|
||||
color: kWhite,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(_lang.add, style: TextStyle(color: kWhite, fontSize: 12, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStateColor.resolveWith((states) => Colors.white),
|
||||
border: TableBorder.all(borderRadius: BorderRadius.circular(2.0), color: kBorderColorTextField),
|
||||
dividerThickness: 1.0,
|
||||
sortAscending: true,
|
||||
showCheckboxColumn: false,
|
||||
horizontalMargin: 5.0,
|
||||
columnSpacing: 10,
|
||||
dataRowMinHeight: 45,
|
||||
showBottomBorder: true,
|
||||
checkboxHorizontalMargin: 0.0,
|
||||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
label: Text(
|
||||
lang.S.of(context).name,
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'${_lang.taxRates} %',
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
_lang.subTaxes,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
headingRowAlignment: MainAxisAlignment.center,
|
||||
label: Text(
|
||||
_lang.action,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
rows: List.generate(
|
||||
groupTaxes.length,
|
||||
(index) => DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
Text(
|
||||
groupTaxes[index].name ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(color: kGreyTextColor),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Center(
|
||||
child: Text(
|
||||
'${groupTaxes[index].rate.toString()}%',
|
||||
textAlign: TextAlign.start,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: kGreyTextColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Wrap(
|
||||
children: List.generate(
|
||||
groupTaxes[index].subTax?.length ?? 0,
|
||||
(i) {
|
||||
return Text(
|
||||
"${groupTaxes[index].subTax?[i].name ?? 'n/a'}, ",
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: kGreyTextColor),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(50, 25),
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
elevation: 1.0,
|
||||
foregroundColor: kGreyTextColor.withValues(alpha: 0.1),
|
||||
shadowColor: kMainColor,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: const TextStyle(color: Colors.white, fontFamily: 'Display', fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => AddGroupTax(taxModel: groupTaxes[index])),
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
FeatherIcons.edit,
|
||||
size: 15,
|
||||
color: kWhite,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
lang.S.of(context).edit,
|
||||
//'Edit',
|
||||
style: const TextStyle(color: kWhite, fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
minimumSize: Size(50, 25),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
elevation: 1.0,
|
||||
foregroundColor: Colors.white.withValues(alpha: 0.1),
|
||||
shadowColor: Colors.red,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: const TextStyle(color: kWhite)),
|
||||
onPressed: () async {
|
||||
if (!permissionService.hasPermission(Permit.vatsDelete.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text('You do not have permission to delete tax.'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
bool result = await showDeleteConfirmationDialog(context: context, itemName: 'vat_&_tax');
|
||||
if (result) {
|
||||
EasyLoading.show(status: _lang.deleting);
|
||||
final repo = TaxRepo();
|
||||
try {
|
||||
final result = await repo.deleteTax(id: groupTaxes[index].id.toString(), context: context, ref: ref);
|
||||
if (result) {
|
||||
ref.refresh(taxProvider);
|
||||
EasyLoading.showSuccess(_lang.deletedSuccessFully);
|
||||
} else {
|
||||
EasyLoading.showError(_lang.failedToDeleteTheTax);
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.showError('${_lang.errorDeletingTax}: $e');
|
||||
} finally {
|
||||
EasyLoading.dismiss();
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.delete_outline,
|
||||
size: 17,
|
||||
color: kWhite,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
lang.S.of(context).delete,
|
||||
//'Delete',
|
||||
style: const TextStyle(color: kWhite, fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
color: WidgetStateColor.resolveWith(
|
||||
(Set<WidgetState> states) {
|
||||
// Use index to determine whether the row is even or odd
|
||||
return index % 2 == 0 ? Colors.grey.shade100 : Colors.white;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
error: (e, stackTrace) {
|
||||
return Center(
|
||||
child: Text(e.toString()),
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
));
|
||||
}, error: (e, stack) {
|
||||
return Text(e.toString());
|
||||
}, loading: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user