Siopas Inventory PETI for ISTW Mobile
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

990 lines
46 KiB

import 'dart:convert';
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:siopas/models/asset_status_model.dart';
import 'package:siopas/models/condition_peti_model.dart';
import 'package:siopas/models/customer_model.dart';
import 'package:siopas/models/type_peti_model.dart';
import 'package:siopas/pages/peminjaman_barang/conn/syncronize.dart';
import 'package:siopas/pages/pengembalian_barang/conn/syncronize.dart';
import 'package:siopas/pages/pengembalian_barang/controller/pengembalian_controller.dart';
import 'package:siopas/services/controllerApi.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:siopas/models/m_asset_status_model.dart';
import 'package:siopas/models/warehouse_mode.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:intl/date_symbol_data_local.dart'; // Import package intl
import 'package:uuid/uuid.dart';
import 'package:dropdown_search/dropdown_search.dart';
import '../../connection/connection.dart';
import 'package:http/http.dart' as http;
import '../../models/user_model.dart';
import '../../providers/auth_provider.dart';
import '../peminjaman_barang/controller/peminjaman_controller.dart';
import 'package:collection/collection.dart';
import 'edit-finish.dart';
// import 'conn/syncronizeAPI.dart';
// import 'controller/peminjaman_controller.dart';
class CreatePengembalianBarangPage extends StatefulWidget {
const CreatePengembalianBarangPage({Key? key}) : super(key: key);
@override
State<CreatePengembalianBarangPage> createState() =>
_CreatePengembalianBarangPageState();
}
class _CreatePengembalianBarangPageState
extends State<CreatePengembalianBarangPage> {
String? token;
PetiAssetModel? petiSqfliteApi;
WarehouseModel? warehouseSqfliteApi;
List<PetiAssetModel>? _valpeti;
List<PetiAssetModel> _filteredPetiList = [];
bool isQRCodeScanned = false;
String searchText = '';
PetiAssetModel? selectedPeti;
List<PetiAssetModel>? selectedPetiList = [];
List<PetiAssetModel> scannedPetiList = [];
List<PetiAssetModel> localSelectedPetiList = [];
List<AssetStatusModel> temporaryDataList = [];
List<AssetStatusModel>? _data;
final _formKey = GlobalKey<FormState>();
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
Barcode? result;
QRViewController? controller;
bool loading = true;
TextEditingController searchBoxController = TextEditingController();
@override
void initState() {
super.initState();
_getUserToken();
petiListAPI();
initializeDateFormatting('id_ID', null);
assetStatusListSqflite();
getSelectedPetiListFromSharedPreferences().then((retrievedPetiList) {
setState(() {
// Update the state with the retrieved list
selectedPetiList = retrievedPetiList;
loading = false;
});
});
}
void _getUserToken() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (mounted) {
setState(() {
token = prefs.getString('token');
loading = false;
});
}
}
// Function to save selectedPetiList to SharedPreferences
void saveSelectedPetiListToSharedPreferences(
List<PetiAssetModel> selectedPetiList) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Map<String, dynamic>> petiListJson =
selectedPetiList.map((peti) => peti.toJson()).toList();
prefs.setStringList('petiListPengembalian',
petiListJson.map((petiJson) => json.encode(petiJson)).toList());
}
// Function to retrieve selectedPetiList from SharedPreferences
Future<List<PetiAssetModel>>
getSelectedPetiListFromSharedPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String>? petiListJson = prefs.getStringList('petiListPengembalian');
if (petiListJson != null) {
List<PetiAssetModel> selectedPetiList = petiListJson
.map((petiJson) => PetiAssetModel.fromJson(json.decode(petiJson)))
.toList();
return selectedPetiList;
} else {
return [];
}
}
void _handlePetiScanResult(int petiId) {
bool isPetiInAssetStatusList =
_data?.any((assetStatus) => assetStatus.peti_id == petiId) ?? false;
bool isPetiInSelectedPetiList =
selectedPetiList!.any((peti) => peti.id == petiId);
PetiAssetModel? petiSqfliteApi = _valpeti?.firstWhereOrNull(
(peti) => peti.id == petiId,
);
String fixLot = petiSqfliteApi?.fix_lot ??
''; // Menyimpan nilai fix_lot jika petiSqfliteApi tidak null
if (isPetiInAssetStatusList) {
showErrorMessage(
'Peti dengan $fixLot sudah terdaftar dalam daftar menu pengembalian. Harap periksa kembali.',
Colors.yellow[700]!,
);
} else if (!isPetiInSelectedPetiList) {
PetiAssetModel? allowedPeti = _valpeti?.firstWhereOrNull(
(peti) =>
peti.id == petiId &&
peti.deleted_at != true &&
peti.status == 'AKTIF',
);
if (allowedPeti != null) {
setState(() {
isQRCodeScanned = true;
_filteredPetiList = [allowedPeti];
petiSqfliteApi = allowedPeti;
selectedPetiList!.add(allowedPeti);
saveSelectedPetiListToSharedPreferences(selectedPetiList!);
});
} else {
showErrorMessage(
'Data Peti tidak ditemukan atau tidak sesuai dengan hak akses.',
Colors.red[700]!,
);
setState(() {
isQRCodeScanned = false;
petiSqfliteApi = null;
});
}
} else {
showErrorMessage(
'Peti dengan $fixLot sudah ada dalam daftar pilihan List Peti. Harap periksa kembali.',
Colors.yellow[700]!,
);
}
}
void _onQRViewCreated(QRViewController controller) async {
this.controller = controller;
bool scanned = false;
// Code to auto-focus the back camera
controller.flipCamera();
await Future.delayed(const Duration(milliseconds: 400));
controller.flipCamera();
controller.scannedDataStream.listen(
(scanData) async {
if (!scanned) {
try {
result = scanData;
List<String> lines = result!.code!.split('\n');
String idPeti = '';
String idWarehouse = '';
for (String line in lines) {
if (line.contains(';')) {
List<String> values = line.split(';');
if (values.length >= 3) {
idPeti = values[1];
idWarehouse = values[2];
break;
}
}
}
int? petiId = int.tryParse(idPeti);
if (petiId != null) {
_handlePetiScanResult(petiId);
} else {
showErrorMessage(
'Nilai QR Code tidak sesuai dengan yang diharapkan.',
Colors.yellow[700]!,
);
setState(() {
isQRCodeScanned = false;
petiSqfliteApi = null;
});
}
scanned = true;
} catch (e) {
controller.stopCamera();
setState(() {
petiSqfliteApi = null;
});
print('Error scanning QR Code: $e');
showErrorMessage(e.toString(), Colors.red[700]!);
} finally {
controller.stopCamera();
await Future.delayed(Duration(milliseconds: 500));
if (mounted) {
Navigator.of(context).pop();
}
}
}
},
);
}
void showErrorMessage(String message, Color backgroundColor) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
message,
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
backgroundColor: backgroundColor,
),
);
}
Future assetStatusListSqflite() async {
if (mounted) {
await ControllerPengembalian()
.fetchPengembalianLocalController()
.then((value) {
setState(() {
_data = (value as List<dynamic>)
.map((e) => AssetStatusModel.fromJson(e))
.toList();
loading = false;
});
});
}
}
Future petiListAPI() async {
if (mounted) {
await ControllerApi().fetchPetiDataAPI().then((value) {
setState(() {
_valpeti = (value as List<dynamic>)
.map((item) => PetiAssetModel.fromJson(item))
.toList();
loading = false;
});
});
}
}
Future<List<PetiAssetModel>> _petiListAPI() async {
List petiList = await ControllerApi().fetchPetiDataAPI();
return petiList.map((item) => PetiAssetModel.fromJson(item)).toList();
}
@override
Widget build(BuildContext context) {
AuthProvider authProvider =
Provider.of<AuthProvider>(context, listen: false);
UserModel user = authProvider.user;
Future<void> saveAssetData() async {
print('Entering saveAssetData');
DateTime now = DateTime.now().toLocal();
String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(now);
// Cek apakah ada item yang dipilih di dalam selectedPetiList
if (selectedPetiList!.any((peti) => peti == null)) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Pilih setidaknya satu peti"),
backgroundColor: Colors.red,
));
print('Exiting saveAssetData - No peti selected');
return;
}
// Iterasi melalui selectedPetiList
for (var peti in selectedPetiList!) {
// Buat model baru
AssetStatusModel assetAddModel = AssetStatusModel(
id: null,
peti_id: peti!.id,
// Tambahkan properti lainnya sesuai kebutuhan
);
int index =
temporaryDataList.indexWhere((data) => data.peti_id == peti.id);
if (index != -1) {
// Jika peti_id sudah ada, ganti entri yang sudah ada
temporaryDataList[index] = assetAddModel;
} else {
// Jika peti_id belum ada, tambahkan ke dalam list
temporaryDataList.add(assetAddModel);
}
}
print('Temporary data added. Length: ${temporaryDataList.length}');
// Navigasi ke halaman selanjutnya dengan membawa data temporaryDataList
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditFinishPengembalian(
temporaryDataList: temporaryDataList,
),
),
);
print('Exiting saveAssetData');
}
Future<void> checkAndAddPeti(PetiAssetModel value) async {
// print('Checking and adding Peti: ${value.fix_lot}');
List assetStatusList =
await ControllerPengembalian().fetchPengembalianLocalController();
// Ensure assetStatusList is of type List
if (assetStatusList is List) {
// Convert JSON strings to AssetStatusModel objects
assetStatusList = assetStatusList
.map((item) =>
AssetStatusModel.fromJson(item as Map<String, dynamic>))
.toList();
}
bool isPetiInSqflite =
assetStatusList.any((assetStatus) => assetStatus.peti_id == value.id);
bool isPetiInLocalList = localSelectedPetiList.contains(value);
bool isPetiInSelectedPetiList =
selectedPetiList!.any((peti) => peti.id == value.id);
if (isPetiInSqflite) {
// print('Peti is already in Sqflite.');
showErrorMessage(
'Peti dengan ${value.fix_lot} sudah ada dalam daftar menu pengembalian. Harap periksa kembali.',
Colors.yellow[700]!,
);
} else if (isPetiInSelectedPetiList || isPetiInLocalList) {
// print('Peti is already in selectedPetiList.');
showErrorMessage(
'Peti dengan ${value.fix_lot} sudah ada dalam daftar pilihan List Peti. Harap periksa kembali.',
Colors.yellow[700]!,
);
} else {
// If the item passes all conditions, add it to the list
print('Adding Peti to the list.');
setState(() {
selectedPetiList!.add(value);
saveSelectedPetiListToSharedPreferences(selectedPetiList!);
});
}
}
// Function to remove peti data from SharedPreferences
Future<void> removePetiFromSharedPreferences(
PetiAssetModel removedPeti) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// Retrieve existing data from SharedPreferences
List<String>? petiListJson = prefs.getStringList('petiListPengembalian');
if (petiListJson != null) {
// Convert JSON strings to PetiAssetModel objects
List<PetiAssetModel> existingPetiList = petiListJson
.map((petiJson) => PetiAssetModel.fromJson(json.decode(petiJson)))
.toList();
// Remove the specified peti from the list
existingPetiList.removeWhere((peti) => peti.id == removedPeti.id);
// Save the updated list back to SharedPreferences
List<String> updatedPetiListJson =
existingPetiList.map((peti) => json.encode(peti.toJson())).toList();
prefs.setStringList('petiListPengembalian', updatedPetiListJson);
}
}
void removePeti(int index) async {
PetiAssetModel? selectedPeti =
selectedPetiList != null ? selectedPetiList![index] : null;
print(
'Before Remove: ${selectedPeti?.fix_lot}'); // Cetak atribut tertentu sebelum penghapusan
if (index >= 0 && index < selectedPetiList!.length) {
if (mounted) {
// Simpan peti yang akan dihapus
PetiAssetModel removedPeti = selectedPetiList![index];
// Hapus item dari selectedPetiList
selectedPetiList!.removeAt(index);
// Hapus entri yang sesuai dari temporaryDataList berdasarkan peti_id
temporaryDataList
.removeWhere((data) => data.peti_id == removedPeti.id);
// Hapus data dari SharedPreferences
await removePetiFromSharedPreferences(removedPeti);
print(
'After Remove: ${selectedPeti?.fix_lot}'); // Cetak daftar setelah penghapusan
}
} else {
print('Invalid index: $index. Tidak ada penghapusan yang dilakukan.');
}
}
var scanArea = (MediaQuery.of(context).size.width < 400 ||
MediaQuery.of(context).size.height < 400)
? 250.0
: 300.0;
return WillPopScope(
onWillPop: () async {
// Mencegah kembali ke halaman sebelumnya
return false;
},
child: Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.indigo[700],
elevation: 0,
title: Text('Buat Peti In',
style: TextStyle(
fontSize: 16,
)),
),
body: loading
? Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: Colors.grey.withOpacity(0.8),
width:
1), // Tambahkan ini untuk menambahkan border
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin:
EdgeInsets.only(left: 8, right: 8, top: 13),
child: Text(
'Tambah List Peti / Scan Peti:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
Row(
children: [
Expanded(
child: Container(
margin: EdgeInsets.only(left: 8),
child:
FutureBuilder<List<PetiAssetModel>>(
future: _petiListAPI(),
builder: (context,
AsyncSnapshot<List<PetiAssetModel>>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child:
CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return Text(
'Error: ${snapshot.error}');
} else {
List<PetiAssetModel> filteredItems =
snapshot.data?.where((peti) {
// Replace the condition with your logic
return !localSelectedPetiList
.contains(
peti.id) &&
!scannedPetiList
.contains(
peti.id);
}).toList() ??
[];
return DropdownSearch<
PetiAssetModel>(
popupProps:
PopupProps.bottomSheet(
showSearchBox: true,
itemBuilder: (context,
PetiAssetModel? peti,
bool? isSelected) {
if (peti == null) {
return SizedBox.shrink();
}
String itemAsString(
PetiAssetModel item) {
return item.fix_lot ?? '';
}
// Convert the item to a string for comparison
String petiAsString =
itemAsString(peti);
// Check if the selected item is already in the list obtained from SharedPreferences
bool
isPetiInSharedPreferences =
localSelectedPetiList.any(
(selectedPeti) =>
itemAsString(
selectedPeti) ==
petiAsString);
// Check if the selected item is already in the list of scanned peti IDs
bool isPetiInScannedList =
scannedPetiList.any(
(scannedPeti) =>
itemAsString(
scannedPeti) ==
petiAsString);
// Include the item only if it's not in either list
if (!isPetiInSharedPreferences &&
!isPetiInScannedList) {
// Tentukan warna dan ikon berdasarkan kondisi status
Color bulletColor =
peti.status == 'AKTIF'
? Colors.green
: Colors.red;
IconData bulletIcon =
peti.status == 'AKTIF'
? Icons.circle
: Icons.circle;
return Container(
child: Column(
children: [
ListTile(
title: Text(
peti.fix_lot
.toString(),
style: TextStyle(
fontSize: 16,
fontFamily:
'OpenSansCondensed',
),
),
leading: Icon(
bulletIcon,
color:
bulletColor,
size: 24,
),
),
Divider(
height: 1,
color: Colors
.grey, // Warna pembatas (divider)
),
],
),
color: peti.id ==
petiSqfliteApi?.id
? Colors.grey
.withOpacity(0.7)
: Colors.white,
);
} else {
// Return an empty container for items that should be hidden
return SizedBox.shrink();
}
},
fit: FlexFit.loose,
title: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Pilih Peti',
style: TextStyle(
fontSize: 18,
fontWeight:
FontWeight
.bold),
),
IconButton(
icon: Icon(
Icons.close,
color: Colors.red,
),
onPressed: () {
Future.delayed(
Duration.zero,
() {
Navigator.pop(
context);
});
},
),
],
),
Divider(),
],
),
),
),
dropdownDecoratorProps:
DropDownDecoratorProps(
dropdownSearchDecoration:
InputDecoration(
hintText: 'Pilih Peti',
border: OutlineInputBorder(
borderRadius:
BorderRadius.all(
Radius.circular(5.0),
),
),
),
),
items: filteredItems,
itemAsString:
(PetiAssetModel item) =>
item.fix_lot ?? '',
onBeforeChange: (PetiAssetModel?
from,
PetiAssetModel? to) async {
// The logic here should remain the same
bool isPetiInSharedPreferences =
localSelectedPetiList
.contains(to);
bool isPetiInScannedList =
scannedPetiList
.contains(to?.id);
if (isPetiInSharedPreferences ||
isPetiInScannedList) {
showErrorMessage(
'Peti dengan ${to?.fix_lot} sudah ada dalam daftar pilihan List Peti. Harap periksa kembali.',
Colors.yellow[700]!,
);
// Wait for a short time to display the error message before returning false
await Future.delayed(Duration(
milliseconds: 500));
return false; // Cancel the change
}
return true; // Allow the change
},
onChanged: (PetiAssetModel?
value) async {
if (value != null) {
bool
isPetiInSharedPreferences =
localSelectedPetiList
.contains(value);
bool isPetiInScannedList =
scannedPetiList
.contains(value.id);
if (isPetiInSharedPreferences ||
isPetiInScannedList) {
print(
'Peti is already in SharedPreferences or Scanned List.');
showErrorMessage(
'Peti dengan ${value.fix_lot} sudah ada dalam daftar pilihan List Peti. Harap periksa kembali.',
Colors.yellow[700]!,
);
} else {
// Move the asynchronous work outside the setState
await checkAndAddPeti(
value);
}
}
},
);
}
},
),
),
),
SizedBox(width: 8), // Spacer antara dua card
Container(
margin: EdgeInsets.all(8),
height:
MediaQuery.of(context).size.height / 10,
decoration: BoxDecoration(
color: Colors.indigoAccent,
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: Colors.white,
width: 3,
),
),
child: IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled:
true, // Set modal menjadi fullscreen
builder: (BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Container(
height: double.infinity,
width: double.infinity,
child: QRView(
key: qrKey,
onQRViewCreated:
_onQRViewCreated,
overlay:
QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea,
),
),
),
Positioned(
bottom: 30,
height: 60,
child: Container(
height:
60, // Lebar dan tinggi sesuai kebutuhan
width: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors
.red, // Warna merah untuk close
),
child: IconButton(
onPressed: () {
Navigator.of(context)
.pop();
},
icon: Icon(
Icons.close,
size: 40,
),
color: Colors.white,
),
),
),
],
);
},
);
},
icon: Icon(
Icons.qr_code,
size: 30,
),
color: Colors.white, // Warna ikon
),
),
],
),
],
),
),
Divider(),
SizedBox(height: 8),
Visibility(
visible: selectedPetiList!.isEmpty == false,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(left: 8, bottom: 8),
child: Text(
'List Peti:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: Colors.grey.withOpacity(0.8),
width:
1), // Tambahkan ini untuk menambahkan border
),
child: Container(
margin: EdgeInsets.all(8),
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: selectedPetiList!.length,
itemBuilder: (BuildContext context, int i) {
return Container(
margin: EdgeInsets.only(bottom: 8),
padding: EdgeInsets.all(8),
child: Row(
children: [
Text(
'${i + 1}.',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 8),
Expanded(
child: Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(
5.0),
border: Border.all(
color: Colors.grey),
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
selectedPetiList![i]
?.fix_lot ??
'Belum dipilih',
style: TextStyle(
fontSize: 16,
fontFamily:
'OpenSansCondensed'),
),
],
),
),
),
Container(
margin: EdgeInsets.only(left: 8),
width: 30,
height: 30,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
),
child: InkWell(
onTap: () {
setState(() {
removePeti(i);
});
},
child: Center(
child: Icon(
Icons.delete,
color: Colors.white,
),
),
),
),
],
),
);
},
),
),
),
],
),
),
],
),
),
),
),
bottomNavigationBar: BottomAppBar(
height: MediaQuery.of(context).size.height / 10,
color: Color.fromARGB(255, 5, 28, 158),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: MediaQuery.of(context).size.width / 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 50, // Lebar dan tinggi sesuai kebutuhan
width: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red, // Warna merah untuk close
),
child: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.close, color: Colors.white),
),
),
],
),
),
Container(
width: MediaQuery.of(context).size.width / 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: IconButton(
onPressed: () async {
// Cek apakah ada setidaknya satu peti terpilih
if (selectedPetiList!.isEmpty ||
selectedPetiList!.any((peti) => peti == null)) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
"Tidak ada peti yang dipilih",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
backgroundColor: Colors.yellow[700],
));
return;
}
await saveAssetData();
},
icon: Icon(Icons.save, color: Colors.white),
),
),
],
),
),
],
),
),
),
);
}
}