refactor : rename files and update routes for poultry actions

feat : poultry kill registration and poultry OrderList

** Made With Nima **
This commit is contained in:
2025-09-16 15:47:35 +03:30
parent 87a0955697
commit e933d22f8f
81 changed files with 22780 additions and 192 deletions

View File

@@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:rasadyar_chicken/data/models/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/data/models/response/hatching_report/hatching_report.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/active_hatching/logic.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_core/presentation/widget/list_item/list_item2.dart';
@@ -17,7 +18,7 @@ class ActiveHatchingPage extends GetView<ActiveHatchingLogic> {
hasSearch: false,
hasFilter: false,
routes: controller.routesName,
onBackPressed: () => Get.back(id: 0),
onBackPressed: () => Get.back(id: poultryFirstKey),
widgets: [
hatchingWidget()
],
@@ -127,11 +128,11 @@ class ActiveHatchingPage extends GetView<ActiveHatchingLogic> {
buildRow(title: 'شماره مجوز جوجه ریزی', value: item.licenceNumber ?? 'N/A'),
buildRow(
title: 'حجم جوجه ریزی',
value: item.quantity.separatedByComma,
value: item.quantity.separatedByCommaFa,
valueStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
buildRow(title: 'مانده در سالن', value: item.leftOver.separatedByComma),
buildRow(title: 'تلفات', value: item.losses.separatedByComma),
buildRow(title: 'مانده در سالن', value: item.leftOver.separatedByCommaFa),
buildRow(title: 'تلفات', value: item.losses.separatedByCommaFa),
buildRow(
title: 'دامپزشک فارم',
value: '${item.vetFarm?.vetFarmFullName}(${item.vetFarm?.vetFarmMobile})',

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/models/response/poultry/poultry_farm.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_farm/poultry_farm.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/home/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/logic.dart';
import 'package:rasadyar_core/core.dart';

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/models/response/poultry/poultry_farm.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_farm/poultry_farm.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_core/core.dart';
@@ -14,7 +16,7 @@ class FarmPage extends GetView<FarmLogic> {
routes: controller.routes,
hasFilter: false,
hasSearch: false,
onBackPressed: () => Get.back(id: 0),
onBackPressed: () => Get.back(id: poultryFirstKey),
widgets: [firstTagInformation(), farmListWidget()],
);
}
@@ -131,7 +133,7 @@ class FarmPage extends GetView<FarmLogic> {
title: 'دامپزشک فارم',
value: '${item.vetFarm?.fullName ?? '-'} (${item.vetFarm?.mobile ?? '-'})',
),
buildRow(title: 'ظرفیت فارم', value: item.totalCapacity.separatedByComma),
buildRow(title: 'ظرفیت فارم', value: item.totalCapacity.separatedByCommaFa),
buildRow(
title: 'جوجه ریزی فعال (تعداد دوره) ',
value:

View File

@@ -0,0 +1,86 @@
import 'package:rasadyar_chicken/data/models/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class GenocideLogic extends GetxController {
List<String> routes = ['اقدام', 'درخواست کشتارها'];
var tokenService = Get.find<TokenStorageService>();
var gService = Get.find<GService>();
var rootLogic = Get.find<PoultryScienceRootLogic>();
Rx<Resource<PaginationModel<PoultryOrder>>> poultryOrderList =
Resource<PaginationModel<PoultryOrder>>.loading().obs;
RxList<int> isExpandedList = <int>[].obs;
final RxInt currentPage = 1.obs;
final RxBool isLoadingMore = false.obs;
Rx<Jalali> fromDateFilter = Jalali
.now()
.obs;
Rx<Jalali> toDateFilter = Jalali
.now()
.obs;
RxnString searchedValue = RxnString();
/* final RxBool isLoadingMoreAllocationsMade = false.obs;
final RxBool isOnLoadingSubmitOrEdit = false.obs;*/
@override
void onReady() {
super.onReady();
getPoultryOrderList();
}
@override
void onClose() {
// TODO: implement onClose
super.onClose();
}
Future<void> getPoultryOrderList([bool loadingMore = false]) async {
if (loadingMore) {
isLoadingMore.value = true;
} else {
poultryOrderList.value = Resource<PaginationModel<PoultryOrder>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1;
}
await safeCall(
call: () =>
rootLogic.poultryRepository.getPoultryOderList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
pageSize: 20,
page: currentPage.value,
search: 'filter',
role: gService.getRole(Module.chicken),
value: searchedValue.value,
fromDate: fromDateFilter.value.toDateTime(),
toDate: toDateFilter.value.toDateTime(),
queryParams: {'today': null}
),
),
onSuccess: (res) async {
await Future.delayed(Duration(milliseconds: 500));
if ((res?.count ?? 0) == 0) {
poultryOrderList.value = Resource<PaginationModel<PoultryOrder>>.empty();
} else {
poultryOrderList.value = Resource<PaginationModel<PoultryOrder>>.success(
PaginationModel<PoultryOrder>(
count: res?.count ?? 0,
next: res?.next,
previous: res?.previous,
results: [...(poultryOrderList.value.data?.results ?? []), ...(res?.results ?? [])],
),
);
}
},
);
}
}

View File

@@ -0,0 +1,222 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class GenocidePage extends GetView<GenocideLogic> {
const GenocidePage({super.key});
@override
Widget build(BuildContext context) {
return BasePage(
routes: controller.routes,
hasSearch: false,
hasFilter: false,
onBackPressed: () => Get.back(id: poultryFirstKey),
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
floatingActionButton: RFab.add(
onPressed: () {
Get.toNamed(ChickenRoutes.killingRegistrationPoultryScience, id: poultryFirstKey);
},
),
widgets: [poultryOrderListWidget()],
);
}
Widget poultryOrderListWidget() {
return Expanded(
child: ObxValue((data) {
return RPaginatedListView(
listType: ListType.separated,
resource: data.value,
hasMore: data.value.data?.next != null,
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
itemBuilder: (context, index) {
var item = data.value.data!.results![index];
return ObxValue((val) {
return ExpandableListItem2(
selected: val.contains(index),
onTap: () => controller.isExpandedList.toggle(index),
index: index,
child: itemListWidget(item),
secondChild: itemListExpandedWidget(item),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.cubeScanSvg.path,
);
}, controller.isExpandedList);
},
itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async => controller.getPoultryOrderList(true),
onRefresh: () async {
controller.currentPage.value = 1;
await controller.getPoultryOrderList();
},
);
}, controller.poultryOrderList),
);
}
Container itemListExpandedWidget(PoultryOrder item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
item.poultry?.unitName ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
Spacer(),
Visibility(
child: Text(
'${item.poultry?.address?.province?.name} / ${item.poultry?.address?.city?.name}',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
),
],
),
Container(
height: 32,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: AppColor.blueLightHover),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'سن مرغ: ${item.hatching?.age ?? '-'}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
Text(
item.sendDate?.formattedJalaliDate ?? '-',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
Text(
'تعداد:${item.quantity.separatedByComma ?? '-'} (قطعه)',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
buildRow(title: 'کد سفارش', value: '${item.orderCode} '),
buildRow(
title: 'نوع فروش',
value: (item.freeSaleInProvince ?? false) ? 'آزاد' : 'دولتی ',
),
buildRow(title: 'نوع کشتار ', value: getKillType(item)),
buildRow(title: 'درخواست', value: getRequestType(item) ?? 'N/A'),
buildRow(title: 'میانگین وزنی', value: '${(item.indexWeight)} (کیلوگرم)'),
buildRow(title: 'قیمت مرغدار', value: '${item.amount.separatedByComma} (ریال)'),
buildRow(title: 'مانده در سالن ', value: '${item.hatching?.leftOver.separatedByComma ?? 0} (قطعه)'),
buildRow(title: ' وضعیت', value: getState(item)),
],
),
);
}
Widget itemListWidget(PoultryOrder item) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 20),
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 3,
children: [
Text(
item.poultry?.unitName ?? 'N/A',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.sendDate?.formattedJalaliDate ?? '-',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
),
],
),
),
Expanded(
flex: 3,
child: Column(
spacing: 3,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'کد سفارش : ${item.orderCode ?? '-'}',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
'تعداد:${item.quantity.separatedByComma ?? '-'} (قطعه)',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
Expanded(
flex: 1,
child: Assets.vec.scanSvg.svg(
width: 32.w,
height: 32.h,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
],
);
}
}
String getRequestType(PoultryOrder item) {
if (item.market ?? false) {
return 'پنل معاملات';
} else if (item.union ?? false) {
return 'اتحادیه';
} else {
return 'خرید مستقیم';
}
}
String getKillType(PoultryOrder item) {
if (item.export ?? false) {
return 'صادرات';
} else if (item.freezing ?? false) {
return 'انجماد';
} else {
return 'عادی';
}
}
String getState(PoultryOrder item) {
if (item.stateProcess == 'pending') {
return 'در انتظار تایید';
} else {
return 'تایید شده';
}
}

View File

@@ -116,13 +116,13 @@ class PoultryScienceHomeLogic extends GetxController {
tagInfo['first'] = tagInfo['first']!.map((tag) {
if (tag.labelTitle == 'کل فارم ها') {
return tag.copyWith(
value: result.farmCount?.separatedByComma ?? '0',
value: result.farmCount?.separatedByCommaFa ?? '0',
isLoading: false,
);
}
if (tag.labelTitle == 'تعداد جوجه ریزی') {
return tag.copyWith(
value: result.hatchingCount?.separatedByComma ?? '0',
value: result.hatchingCount?.separatedByCommaFa ?? '0',
isLoading: false,
);
}
@@ -134,12 +134,12 @@ class PoultryScienceHomeLogic extends GetxController {
switch (tag.labelTitle) {
case 'حجم جوجه ریزی':
return tag.copyWith(
value: result.hatchingQuantity?.separatedByComma ?? '0',
value: result.hatchingQuantity?.separatedByCommaFa ?? '0',
isLoading: false,
);
case 'مانده در سالن':
return tag.copyWith(
value: result.hatchingLeftOver?.separatedByComma ?? '0',
value: result.hatchingLeftOver?.separatedByCommaFa ?? '0',
isLoading: false,
);
default:
@@ -152,12 +152,12 @@ class PoultryScienceHomeLogic extends GetxController {
switch (tag.labelTitle) {
case 'تلفات':
return tag.copyWith(
value: result.hatchingLosses?.separatedByComma ?? '0',
value: result.hatchingLosses?.separatedByCommaFa ?? '0',
isLoading: false,
);
case 'حجم کشتار شده':
return tag.copyWith(
value: result.hatchingKilledQuantity?.separatedByComma ?? '0',
value: result.hatchingKilledQuantity?.separatedByCommaFa ?? '0',
isLoading: false,
);
default:
@@ -169,12 +169,12 @@ class PoultryScienceHomeLogic extends GetxController {
switch (element.labelTitle) {
case 'کمترین سن جوجه ریزی':
return element.copyWith(
value: result.hatchingMinAge?.separatedByComma ?? '0',
value: result.hatchingMinAge?.separatedByCommaFa ?? '0',
isLoading: false,
);
case 'بیشترین سن جوجه ریزی':
return element.copyWith(
value: result.hatchingMaxAge?.separatedByComma ?? '0',
value: result.hatchingMaxAge?.separatedByCommaFa ?? '0',
isLoading: false,
);
default:

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/models/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/data/models/response/hatching_report/hatching_report.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_chicken/presentation/widget/page_route.dart';
import 'package:rasadyar_core/core.dart';
@@ -18,7 +19,7 @@ class InspectionPoultrySciencePage extends GetView<InspectionPoultryScienceLogic
hasBack: true,
hasFilter: false,
routesWidget: ObxValue((route) => buildPageRoute(route), controller.routesName),
onBackPressed: () => Get.back(id: 0),
onBackPressed: () => Get.back(id: poultryFirstKey),
widgets: [
segmentWidget(),
@@ -132,11 +133,11 @@ class InspectionPoultrySciencePage extends GetView<InspectionPoultryScienceLogic
buildRow(title: 'شماره مجوز جوجه ریزی', value: item.licenceNumber ?? 'N/A'),
buildRow(
title: 'حجم جوجه ریزی',
value: item.quantity.separatedByComma,
value: item.quantity.separatedByCommaFa,
valueStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
buildRow(title: 'مانده در سالن', value: item.leftOver.separatedByComma),
buildRow(title: 'تلفات', value: item.losses.separatedByComma),
buildRow(title: 'مانده در سالن', value: item.leftOver.separatedByCommaFa),
buildRow(title: 'تلفات', value: item.losses.separatedByCommaFa),
buildRow(
title: 'دامپزشک فارم',
value: '${item.vetFarm?.vetFarmFullName}(${item.vetFarm?.vetFarmMobile})',
@@ -425,14 +426,14 @@ class InspectionPoultrySciencePage extends GetView<InspectionPoultryScienceLogic
buildRow(title: 'شماره مجوز جوجه ریزی', value: item.hatching?.licenceNumber ?? 'N/A'),
buildRow(
title: 'حجم جوجه ریزی',
value: item.hatching?.quantity.separatedByComma ?? 'N/A',
value: item.hatching?.quantity.separatedByCommaFa ?? 'N/A',
valueStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
buildRow(
title: 'مانده در سالن',
value: item.hatching?.leftOver.separatedByComma ?? 'N/A',
value: item.hatching?.leftOver.separatedByCommaFa ?? 'N/A',
),
buildRow(title: 'تلفات', value: item.hatching?.losses.separatedByComma ?? 'N/A'),
buildRow(title: 'تلفات', value: item.hatching?.losses.separatedByCommaFa ?? 'N/A'),
buildRow(
title: 'دامپزشک فارم',
value:

View File

@@ -0,0 +1,360 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/models/poultry_export/poultry_export.dart';
import 'package:rasadyar_chicken/data/models/request/kill_registration/kill_registration.dart';
import 'package:rasadyar_chicken/data/models/response/all_poultry/all_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/approved_price/approved_price.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/kill_request_poultry/kill_request_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_hatching/poultry_hatching.dart';
import 'package:rasadyar_chicken/data/models/response/sell_for_freezing/sell_for_freezing.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/logic.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_core/core.dart';
class KillingRegistrationLogic extends GetxController {
List<String> routes = ['اقدام', 'درخواست کشتارها', 'ثبت کشتار'];
var tokenService = Get.find<TokenStorageService>();
var gService = Get.find<GService>();
var rootLogic = Get.find<PoultryScienceRootLogic>();
GlobalKey<FormState> formKey = GlobalKey<FormState>();
Rxn<ApprovedPrice> approvedPrice = Rxn<ApprovedPrice>();
Rx<Jalali> killsDate = Jalali.now().obs;
Rxn<SellForFreezing> sellForFreezing = Rxn<SellForFreezing>();
Rxn<PoultryExport> poultryExport = Rxn<PoultryExport>();
TextEditingController quantityKillsController = TextEditingController();
TextEditingController quantityLoseController = TextEditingController(text: 0.toString());
TextEditingController averageWeightKillsController = TextEditingController();
TextEditingController priceFreeSaleController = TextEditingController();
RxInt generatedApprovedPrice = 0.obs;
RxBool isOnSubmitLoading = false.obs;
RxBool isExportSelected = false.obs;
RxBool isFreezedSelected = false.obs;
RxBool isMarketSelected = false.obs;
RxBool isFreeSale = false.obs;
//step 1
Rx<Resource<List<AllPoultry>>> allPoultryList = Resource<List<AllPoultry>>.loading().obs;
Rxn<AllPoultry> selectedPoultry = Rxn();
//step 2
Rx<Resource<List<KillRequestPoultry>>> poultryList = Resource<List<KillRequestPoultry>>.success(
[],
).obs;
Rxn<KillRequestPoultry> selectedKillRequestPoultry = Rxn();
//step 3
Rx<Resource<List<PoultryHatching>>> poultryHatchingList = Resource<List<PoultryHatching>>.success(
[],
).obs;
Rxn<PoultryHatching> selectedPoultryHatching = Rxn();
//step 5
Rx<Resource<List<KillHousePoultry>>> killHouseList = Resource<List<KillHousePoultry>>.success(
[],
).obs;
Rxn<KillHousePoultry> selectedKillHouse = Rxn();
@override
void onReady() {
super.onReady();
getApprovedPrice();
getAllPoultryList();
getSellForFreezing();
getPoultryExport();
priceListener();
ever(selectedKillRequestPoultry, (callback) {
if (callback?.provinceAllowChooseKillHouse?.allowState ?? false) {
getKillHouseList();
}
});
everAll(
[selectedPoultry, selectedKillRequestPoultry, selectedPoultryHatching, selectedKillHouse],
(callback) {
checkSubmitButton();
},
);
}
@override
void onClose() {
super.onClose();
quantityKillsController.dispose();
quantityLoseController.dispose();
averageWeightKillsController.dispose();
priceFreeSaleController.dispose();
clearSelectedStep1();
clearSelectedStep2();
selectedKillHouse.value = null;
killHouseList.value = Resource<List<KillHousePoultry>>.success([]);
}
void priceListener() {
quantityKillsController.addListener(() {
if (averageWeightKillsController.text.isNotEmpty && quantityKillsController.text.isNotEmpty) {
generatedApprovedPrice.value = calculateApprovedPrice().toInt();
priceFreeSaleController.text = generatedApprovedPrice.value.separatedByComma;
checkSubmitButton();
} else {
generatedApprovedPrice.value = 0;
priceFreeSaleController.text = '0';
checkSubmitButton();
}
});
averageWeightKillsController.addListener(() {
if (averageWeightKillsController.text.isNotEmpty && quantityKillsController.text.isNotEmpty) {
generatedApprovedPrice.value = calculateApprovedPrice().toInt();
priceFreeSaleController.text = generatedApprovedPrice.value.separatedByComma;
checkSubmitButton();
} else {
generatedApprovedPrice.value = 0;
priceFreeSaleController.text = '0';
checkSubmitButton();
}
});
priceFreeSaleController.addListener(() {
final text = priceFreeSaleController.text;
if (text.isNotEmpty) {
generatedApprovedPrice.value = int.parse(text.replaceAll(',', ''));
} else {
generatedApprovedPrice.value = 0;
}
checkSubmitButton();
});
}
void clearSelectedStep1() {
selectedPoultry.value = null;
selectedKillRequestPoultry.value = null;
selectedPoultryHatching.value = null;
poultryList.value = Resource<List<KillRequestPoultry>>.success([]);
poultryHatchingList.value = Resource<List<PoultryHatching>>.success([]);
}
void clearSelectedStep2() {
selectedKillRequestPoultry.value = null;
selectedPoultryHatching.value = null;
poultryHatchingList.value = Resource<List<PoultryHatching>>.success([]);
}
Future<void> getApprovedPrice() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getApprovedPrice(
token: tokenService.accessToken.value ?? '',
),
onSuccess: (result) {
if (result != null) {
approvedPrice.value = result;
}
},
onError: (error, stackTrace) {},
);
}
Future<void> getAllPoultryList() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getAllPoultry(
token: tokenService.accessToken.value ?? '',
queryParameters: buildRawQueryParams(role: gService.getRole(Module.chicken)),
),
onSuccess: (result) {
if (result != null) {
allPoultryList.value = Resource<List<AllPoultry>>.success(result);
}
},
onError: (error, stackTrace) {
allPoultryList.value = Resource<List<AllPoultry>>.error('$error -- $stackTrace');
},
);
}
Future<void> getSellForFreezing() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getSellForFreezing(
token: tokenService.accessToken.value ?? '',
),
onSuccess: (result) {
if (result != null) {
sellForFreezing.value = result;
}
},
onError: (error, stackTrace) {},
);
}
Future<void> getPoultryExport() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getPoultryExport(
token: tokenService.accessToken.value ?? '',
),
onSuccess: (result) {
if (result != null) {
poultryExport.value = result;
}
},
onError: (error, stackTrace) {},
);
}
Future<void> getUserPoultryList() async {
poultryList.value = Resource<List<KillRequestPoultry>>.loading();
await safeCall(
call: () async => await rootLogic.poultryRepository.getUserPoultry(
token: tokenService.accessToken.value ?? '',
queryParameters: buildQueryParams(
value: selectedPoultry.value?.user?.mobile,
queryParams: {'type': 'filter'},
),
),
onSuccess: (result) {
if (result != null) {
poultryList.value = Resource<List<KillRequestPoultry>>.success(result);
}
},
onError: (error, stackTrace) {
poultryList.value = Resource<List<KillRequestPoultry>>.error('$error -- $stackTrace');
},
);
}
Future<void> getPoultryHatchingList() async {
poultryHatchingList.value = Resource<List<PoultryHatching>>.loading();
await safeCall(
call: () async => await rootLogic.poultryRepository.getPoultryHatching(
token: tokenService.accessToken.value ?? '',
queryParameters: buildRawQueryParams(
queryParams: {'key': selectedKillRequestPoultry.value?.key},
),
),
onSuccess: (result) {
if (result != null) {
poultryHatchingList.value = Resource<List<PoultryHatching>>.success(result);
}
},
onError: (error, stackTrace) {
poultryHatchingList.value = Resource<List<PoultryHatching>>.error('$error -- $stackTrace');
},
);
}
Future<void> getKillHouseList() async {
killHouseList.value = Resource<List<KillHousePoultry>>.loading();
await safeCall(
call: () async => await rootLogic.poultryRepository.getKillHouseList(
token: tokenService.accessToken.value ?? '',
queryParameters: buildRawQueryParams(
queryParams: {'show_poultry': '', 'date': DateTime.now().formattedDashedGregorian},
),
),
onSuccess: (result) {
if (result != null) {
killHouseList.value = Resource<List<KillHousePoultry>>.success(result);
}
},
onError: (error, stackTrace) {
killHouseList.value = Resource<List<KillHousePoultry>>.error('$error -- $stackTrace');
},
);
}
double calculateApprovedPrice() {
final inputWeight = double.parse(averageWeightKillsController.text) * 1000;
final lowestWeight = approvedPrice.value?.lowestWeight ?? 0;
final highestWeight = approvedPrice.value?.highestWeight ?? 0;
final lowestPrice = approvedPrice.value?.lowestPrice ?? 0;
final highestPrice = approvedPrice.value?.highestPrice ?? 0;
if (inputWeight <= lowestWeight) {
return lowestPrice;
} else if (inputWeight >= highestWeight) {
return highestPrice;
} else {
final diffWeight = highestWeight - lowestWeight;
final diffPrice = highestPrice - lowestPrice;
final fraction = diffPrice / diffWeight;
final diffFromMinWeight = inputWeight - lowestWeight;
return diffFromMinWeight * fraction + lowestPrice;
}
}
void changeSaleType() {
isFreeSale.value = !isFreeSale.value;
generatedApprovedPrice.value = calculateApprovedPrice().toInt();
}
void checkSubmitButton() {
isOnSubmitLoading.value =
selectedPoultry.value != null &&
selectedKillRequestPoultry.value != null &&
selectedPoultryHatching.value != null &&
quantityKillsController.text.isNotEmpty &&
averageWeightKillsController.text.isNotEmpty &&
((selectedKillRequestPoultry.value?.provinceAllowChooseKillHouse?.mandatory ?? false)
? selectedKillHouse.value != null
: true);
}
Future<void> submitKillRegistration() async {
isOnSubmitLoading.value = false;
if (!formKey.currentState!.validate()) {
return;
}
KillRegistrationRequest registrationRequest = KillRegistrationRequest(
indexWeight: double.parse(averageWeightKillsController.text),
amount: generatedApprovedPrice.value,
approvedPrice: approvedPrice.value?.approved ?? false,
auctionList: [],
cash: true,
chickenBreed: selectedPoultryHatching.value?.chickenBreed,
confirmPoultryMobile: selectedPoultry.value?.user?.mobile,
credit: false,
export: isExportSelected.value,
financialOperation: "outside-system",
freeSaleInProvince: isMarketSelected.value,
freezing: isFreezedSelected.value,
killHouseList: [
if (selectedKillHouse.value != null)
'${selectedKillHouse.value?.name}(${selectedKillHouse.value?.fullname})',
],
killReqKey: selectedKillHouse.value?.killReqKey,
losses: quantityLoseController.text,
market: isMarketSelected.value,
operatorKey: "",
poultryHatchingKey: selectedPoultryHatching.value?.key,
poultryKey: selectedPoultry.value?.key,
quantity: int.parse(quantityKillsController.text),
role: gService.getRole(Module.chicken),
sendDate: killsDate.value.toDateTime().formattedDashedGregorian,
);
await safeCall(
call: () async => await rootLogic.poultryRepository.submitKillRegistration(
token: tokenService.accessToken.value ?? '',
request: registrationRequest,
),
onSuccess: (result) async {
defaultShowSuccessMessage(
'ثبت با موفقیت انجام شد',
onDismissed: () {
Get.back(id: poultryFirstKey);
},
);
},
onError: (error, stackTrace) {},
);
}
}

View File

@@ -0,0 +1,496 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/models/response/all_poultry/all_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/kill_request_poultry/kill_request_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_hatching/poultry_hatching.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class KillingRegistrationPage extends GetView<KillingRegistrationLogic> {
const KillingRegistrationPage({super.key});
@override
Widget build(BuildContext context) {
return BasePageWithScroll(
hasSearch: false,
hasFilter: false,
routes: controller.routes,
onBackPressed: () => Get.back(id: poultryFirstKey),
widgets: [
poultryFarmWidget(),
poultryUserListWidget(),
poultryHatchingWidget(),
ObxValue((data) {
return Visibility(
visible: controller.selectedPoultryHatching.value != null,
child: Form(
key: controller.formKey,
child: Column(
children: [
informationWidget(),
killDateWidget(
date: controller.killsDate,
onChanged: (Jalali jalali) {
controller.killsDate.value = jalali;
},
),
loseWidget(),
quantityKillsWidget(),
averageWeightKillsWidget(),
saleTypeWidget(),
priceWidget(),
buyerListWidget(),
slaughterhouseSelectedWidget(),
submitButtonWidget(),
],
),
),
);
}, controller.selectedPoultryHatching),
],
);
}
Widget informationWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
child: ObxValue(
(data) => Column(
spacing: 8,
children: [
buildUnitRow(
title: 'تعداد جوجه ریزی',
value: data.value?.quantity.separatedByCommaFa,
unit: 'قطعه',
),
buildUnitRow(
title: 'جمع تلفات ثبت شده دامپزشک و مرغدار',
value: data.value?.losses.separatedByCommaFa,
unit: 'قطعه',
),
buildUnitRow(
title: 'باقیمانده',
value: data.value?.leftOver.separatedByCommaFa,
unit: 'قطعه',
),
buildUnitRow(
title: 'سن مرغ',
value: data.value?.chickenAge.separatedByCommaFa,
unit: 'روز',
),
buildUnitRow(
title: 'مجوز فروش آزاد',
value: data.value?.freeGovernmentalInfo?.totalFreeCommitmentQuantity.separatedByCommaFa,
unit: 'قطعه',
),
buildUnitRow(
title: 'مانده فروش آزاد',
value: data
.value
?.freeGovernmentalInfo
?.leftTotalFreeCommitmentQuantity
.separatedByCommaFa,
unit: 'قطعه',
),
],
),
controller.selectedPoultryHatching,
),
);
}
Widget poultryFarmWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
child: ObxValue((data) {
return ResourceOverlayDropdown<AllPoultry>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.clearSelectedStep1();
controller.selectedPoultry.value = value;
controller.getUserPoultryList();
},
selectedItem: controller.selectedPoultry.value,
itemBuilder: (item) => Text(labelPoultry(item), maxLines: 2),
labelBuilder: (item) => Text(labelPoultry(item)),
);
}, controller.allPoultryList),
);
}
String labelPoultry(AllPoultry? item) {
if (item == null) {
return 'انتخاب مرغداری';
} else {
return '${item.unitName} (${item.address?.city?.name}) / ${item.user?.fullname} (${item.user?.mobile}) / ${item.lastHatchingRemainQuantity} قطعه ';
}
}
Widget poultryUserListWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: ObxValue((data) {
return ResourceOverlayDropdown<KillRequestPoultry>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.clearSelectedStep2();
controller.selectedKillRequestPoultry.value = value;
controller.getPoultryHatchingList();
},
selectedItem: controller.selectedKillRequestPoultry.value,
itemBuilder: (item) => Text(item.unitName ?? '-'),
labelBuilder: (item) => Text(item?.unitName ?? 'محل پرورش'),
);
}, controller.poultryList),
);
}
Widget poultryHatchingWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
child: ObxValue((data) {
return ResourceOverlayDropdown<PoultryHatching>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.selectedPoultryHatching.value = value;
},
selectedItem: controller.selectedPoultryHatching.value,
itemBuilder: (item) => Text(labelPoultryHatching(item)),
labelBuilder: (item) => Text(labelPoultryHatching(item)),
);
}, controller.poultryHatchingList),
);
}
String labelPoultryHatching(PoultryHatching? item) {
if (item == null) {
return 'دوره جوجه ریزی';
} else {
return ' دوره ${item.period} سالن ${item.hall} نژاد ${item.chickenBreed} باقیمانده ${item.leftOver} قطعه ';
}
}
Widget killDateWidget({required Rx<Jalali> date, required Function(Jalali jalali) onChanged}) {
return GestureDetector(
onTap: () {
Get.bottomSheet(modalDatePicker(onDateSelected: (value) => onChanged(value)));
},
child: Container(
height: 40,
margin: EdgeInsets.symmetric(horizontal: 20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.darkGreyLight),
),
padding: EdgeInsets.symmetric(horizontal: 11, vertical: 4),
child: Row(
spacing: 8,
children: [
Assets.vec.calendarSvg.svg(
width: 24,
height: 24,
colorFilter: const ColorFilter.mode(AppColor.bgDark, BlendMode.srcIn),
),
Text('تاریخ کشتار', style: AppFonts.yekan14.copyWith(color: AppColor.bgDark)),
Spacer(),
ObxValue((data) {
return Text(
date.value.formatCompactDate(),
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark),
);
}, date),
],
),
),
);
}
Widget loseWidget() {
return Padding(
padding: EdgeInsets.fromLTRB(20.w, 10.h, 20.w, 0),
child: RTextField(
label: 'تعداد تلفات (قطعه)',
filled: true,
filledColor: Colors.white,
keyboardType: TextInputType.number,
controller: controller.quantityLoseController,
),
);
}
Widget quantityKillsWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
child: RTextField(
label: 'تعداد کشتار (قطعه)',
filled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'تعداد کشتار را وارد کنید';
}
final count = double.tryParse(value.replaceAll(',', ''));
if (controller.isFreeSale.value) {
if (count! >
(controller
.selectedPoultryHatching
.value
?.freeGovernmentalInfo
?.leftTotalFreeCommitmentQuantity ??
0)) {
return 'مجوز فروش آزاد شما کافی نیست';
}
} else {
if (count! > (controller.selectedPoultryHatching.value?.leftOver ?? 0)) {
return 'تعداد کشتار نباید بیشتر از باقیمانده جوجه ریزی باشد';
}
}
return null;
},
textInputAction: TextInputAction.next,
filledColor: Colors.white,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly, SeparatorInputFormatter()],
controller: controller.quantityKillsController,
),
);
}
Widget averageWeightKillsWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: RTextField(
label: 'میانگین وزن (کیلوگرم)',
filled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'میانگین وزن را وارد کنید';
}
final weight = double.tryParse(value.replaceAll(',', ''));
if (weight == null || weight <= 0) {
return 'میانگین وزن باید عددی بزرگتر از صفر باشد';
} else if (weight >
(controller.selectedPoultryHatching.value?.managementHatchingAgeRange?.toWeight ??
10000) ||
weight <
(controller
.selectedPoultryHatching
.value
?.managementHatchingAgeRange
?.fromWeight ??
-10000)) {
return 'میانگین وزن باید بین ${controller.selectedPoultryHatching.value?.managementHatchingAgeRange?.fromWeight} تا ${controller.selectedPoultryHatching.value?.managementHatchingAgeRange?.toWeight} کیلوگرم باشد';
}
return null;
},
filledColor: Colors.white,
keyboardType: TextInputType.number,
controller: controller.averageWeightKillsController,
),
);
}
Widget saleTypeWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
child: ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
CustomChip(
title: 'فروش دولتی',
index: 0,
onTap: (int p1) {
controller.changeSaleType();
},
isSelected: !(data.value),
),
CustomChip(
title: 'فروش آزاد',
index: 1,
onTap: (int p1) {
controller.changeSaleType();
},
isSelected: data.value,
),
],
);
}, controller.isFreeSale),
);
}
Widget priceWidget() {
return ObxValue((data){
if(!data.value){
return Container(
height: 40.h,
margin: EdgeInsets.symmetric(horizontal: 20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1.w, color: AppColor.darkGreyLight),
),
padding: EdgeInsets.symmetric(horizontal: 11.w, vertical: 4.h),
child: Row(
spacing: 8,
children: [
Text('قیمت مصوب', style: AppFonts.yekan14.copyWith(color: AppColor.bgDark)),
Spacer(),
ObxValue((data) {
return Text(
' ${data.value.separatedByCommaFa} ریال',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark),
);
}, controller.generatedApprovedPrice),
],
),
);
}else{
return Padding(
padding: EdgeInsets.fromLTRB(20.w, 10.h, 20.w, 0),
child: RTextField(
label: 'قیمت پیشنهادی (ریال)',
validator: (value) {
if (value == null || value.isEmpty) {
return 'قیمت پیشنهادی را وارد کنید';
}
final price = double.tryParse(value.replaceAll(',', ''));
dLog(controller.priceFreeSaleController.text);
fLog(value);
if (price == null || price <= 0) {
return 'قیمت پیشنهادی باید عددی بزرگتر از صفر باشد';
}
return null;
},
filled: true,
filledColor: Colors.white,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly, SeparatorInputFormatter()],
controller: controller.priceFreeSaleController,
),
);
}
}, controller.isFreeSale);
}
Widget buyerListWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
child: ObxValue((data) {
return Visibility(
visible: data.value?.provinceAllowChooseKillHouse?.allowState ?? false,
child: ObxValue((data) {
return ResourceOverlayDropdown<KillHousePoultry>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.selectedKillHouse.value = value;
},
selectedItem: controller.selectedKillHouse.value,
itemBuilder: (item) => Text(buildKillHouseLabel(item)),
labelBuilder: (item) => Text(buildKillHouseLabel(item)),
);
}, controller.killHouseList),
);
}, controller.selectedKillRequestPoultry),
);
}
String buildKillHouseLabel(KillHousePoultry? item) {
if (item == null) {
return 'خریدار/ظرفیت باقیمانده';
} else {
return '${item.name} / ${item.quantitySum} قطعه ';
}
}
Widget slaughterhouseSelectedWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Wrap(
spacing: 10,
runSpacing: 10,
children: [
ObxValue((data) {
return Visibility(
visible: data.value?.permission ?? false,
child: ObxValue(
(data) => CheckBoxChips(
title: 'کشتار برای انجماد',
data: data.value,
onTap: (p1) {
controller.isFreezedSelected.value = !controller.isFreezedSelected.value;
},
isSelected: data.value,
),
controller.isFreezedSelected,
),
);
}, controller.sellForFreezing),
ObxValue((data) {
return CheckBoxChips(
title: 'نمایش در پنل معاملات',
data: 0,
onTap: (int p1) {
controller.isMarketSelected.value = !controller.isMarketSelected.value;
},
isSelected: data.value,
);
}, controller.isMarketSelected),
ObxValue((data) {
return Visibility(
visible: data.value?.allow ?? false,
child: ObxValue((data) {
return CheckBoxChips(
title: 'کشتار برای صادرات',
data: data.value,
onTap: (_) {
controller.isExportSelected.value = !controller.isExportSelected.value;
},
isSelected: data.value,
);
}, controller.isExportSelected),
);
}, controller.poultryExport),
],
),
);
}
Widget submitButtonWidget() {
return ObxValue((data) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.h),
child: RElevated(
enabled: data.value,
height: 45.h,
isFullWidth: true,
backgroundColor: AppColor.greenNormal,
textStyle: AppFonts.yekan16Bold.copyWith(color: Colors.white),
onPressed: () {
controller.submitKillRegistration();
},
text: 'ثبت کشتار',
),
);
}, controller.isOnSubmitLoading);
}
}

View File

@@ -18,7 +18,7 @@ class PoultryActionLogic extends GetxController {
),
PoultryActionItem(
title: "ثبت کشتار",
route: ChickenRoutes.killingRegistrationPoultryScience,
route: ChickenRoutes.genocidePoultryScience,
icon: Assets.vec.noteRemoveSvg.path,
),
PoultryActionItem(

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/app_bar.dart';
import 'package:rasadyar_core/core.dart';
@@ -53,7 +54,7 @@ class PoultryActionPage extends GetView<PoultryActionLogic> {
vecIcon: item.icon,
iconColor: AppColor.blueNormal,
onTap: () async {
Get.toNamed(item.route, id: 0);
Get.toNamed(item.route, id: poultryFirstKey);
},
);
},

View File

@@ -4,6 +4,7 @@ import 'package:rasadyar_chicken/data/repositories/poultry_science/poultry_scien
import 'package:rasadyar_chicken/presentation/pages/common/profile/view.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/home/view.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/poultry_action/view.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
import 'package:rasadyar_core/core.dart';
@@ -12,9 +13,11 @@ enum ErrorLocationType { serviceDisabled, permissionDenied, none }
class PoultryScienceRootLogic extends GetxController {
RxInt currentPage = 1.obs;
List<Widget> pages = [PoultryActionPage(), PoultryScienceHomePage(), ProfilePage()];
late DioRemote dioRemote;
var tokenService = Get.find<TokenStorageService>();
late PoultryScienceRepository poultryRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
@@ -46,4 +49,17 @@ class PoultryScienceRootLogic extends GetxController {
void changePage(int index) {
currentPage.value = index;
}
int getNestedKey() {
switch (currentPage.value) {
case 0:
return poultryFirstKey;
case 1:
return poultrySecondKey;
case 2:
return poultryThirdKey;
default:
return poultryFirstKey;
}
}
}

View File

@@ -18,7 +18,7 @@ class PoultryScienceRootPage extends GetView<PoultryScienceRootLogic> {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) async {
final nestedKey = Get.nestedKey(controller.currentPage.value);
final nestedKey = Get.nestedKey(controller.getNestedKey());
final currentNavigator = nestedKey?.currentState;
if (currentNavigator?.canPop() ?? false) {