|
|
|
@ -1,7 +1,7 @@
|
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:convert';
|
|
|
|
|
|
|
|
import 'dart:developer';
|
|
|
|
import 'dart:io';
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:test_sa/controllers/providers/api/parts_provider.dart';
|
|
|
|
import 'package:test_sa/controllers/providers/api/parts_provider.dart';
|
|
|
|
@ -16,7 +16,6 @@ import 'package:test_sa/models/helper_data_models/spare_part/activity_spare_part
|
|
|
|
import 'package:test_sa/models/lookup.dart';
|
|
|
|
import 'package:test_sa/models/lookup.dart';
|
|
|
|
import 'package:test_sa/models/service_request/spare_parts.dart';
|
|
|
|
import 'package:test_sa/models/service_request/spare_parts.dart';
|
|
|
|
import 'package:test_sa/models/size_config.dart';
|
|
|
|
import 'package:test_sa/models/size_config.dart';
|
|
|
|
import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
|
|
|
|
|
|
|
|
import 'package:test_sa/modules/cm_module/cm_request_utils.dart';
|
|
|
|
import 'package:test_sa/modules/cm_module/cm_request_utils.dart';
|
|
|
|
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
|
|
|
|
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
|
|
|
|
import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart';
|
|
|
|
import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart';
|
|
|
|
@ -33,45 +32,60 @@ import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
|
|
|
|
|
|
|
|
|
|
|
|
class SparePartRequest extends StatefulWidget {
|
|
|
|
class SparePartRequest extends StatefulWidget {
|
|
|
|
static const String id = "/spare-part-request";
|
|
|
|
static const String id = "/spare-part-request";
|
|
|
|
final SparePartHelperModel ?model;
|
|
|
|
final SparePartHelperModel? model;
|
|
|
|
final CMDetailProvider provider;
|
|
|
|
final dynamic provider;
|
|
|
|
|
|
|
|
final bool fromCm;
|
|
|
|
|
|
|
|
|
|
|
|
const SparePartRequest({
|
|
|
|
const SparePartRequest({
|
|
|
|
Key? key,
|
|
|
|
Key? key,
|
|
|
|
required this.model,
|
|
|
|
required this.model,
|
|
|
|
required this.provider,
|
|
|
|
required this.provider,
|
|
|
|
|
|
|
|
this.fromCm = false,
|
|
|
|
}) : super(key: key);
|
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
_SparePartRequestState createState() => _SparePartRequestState();
|
|
|
|
_SparePartRequestState createState() => _SparePartRequestState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class _SparePartRequestState extends State<SparePartRequest> with TickerProviderStateMixin {
|
|
|
|
class _SparePartRequestState extends State<SparePartRequest> {
|
|
|
|
PartsProvider? _partsProvider;
|
|
|
|
PartsProvider? _partsProvider;
|
|
|
|
bool _isLoading = false;
|
|
|
|
bool _isLoading = false;
|
|
|
|
List<SparePart> _spareParts = [];
|
|
|
|
List<SparePart> _spareParts = [];
|
|
|
|
|
|
|
|
List<GenericAttachmentModel> _attachments = [];
|
|
|
|
|
|
|
|
|
|
|
|
List<GenericAttachmentModel> attachments = [];
|
|
|
|
|
|
|
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
|
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
|
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
|
|
|
|
|
final TextEditingController _partQtyController = TextEditingController();
|
|
|
|
|
|
|
|
final TextEditingController _installQtyController = TextEditingController();
|
|
|
|
|
|
|
|
final TextEditingController _returnQtyController = TextEditingController();
|
|
|
|
|
|
|
|
final TextEditingController _oracleNoController = TextEditingController();
|
|
|
|
|
|
|
|
final TextEditingController _descriptionController = TextEditingController();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Lookup statusLookup = Lookup.fromJson({"id": 5619, "name": "New", "value": 1});
|
|
|
|
Lookup statusLookup = Lookup.fromJson({"id": 5619, "name": "New", "value": 1});
|
|
|
|
int? activityStatus;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
super.initState();
|
|
|
|
_partsProvider = Provider.of<PartsProvider>(context, listen: false);
|
|
|
|
_partsProvider = Provider.of<PartsProvider>(context, listen: false);
|
|
|
|
|
|
|
|
_initializeModel();
|
|
|
|
|
|
|
|
_loadInitialData();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize model with existing values or defaults
|
|
|
|
void _initializeModel() {
|
|
|
|
|
|
|
|
final bool isNewActivity = widget.model?.id == null || widget.model?.id == 0;
|
|
|
|
|
|
|
|
if (isNewActivity) {
|
|
|
|
|
|
|
|
widget.provider.sparePartHelperModel = SparePartHelperModel(
|
|
|
|
|
|
|
|
id: 0,
|
|
|
|
|
|
|
|
workOrderId: widget.fromCm ? widget.provider.currentWorkOrder?.data?.requestId : null,
|
|
|
|
|
|
|
|
pmVisitId: !widget.fromCm ? widget.provider.planPreventiveVisit?.id : null,
|
|
|
|
|
|
|
|
sparePartAttachments: [],
|
|
|
|
|
|
|
|
sparePart: SparePart(),
|
|
|
|
|
|
|
|
quantity: null,
|
|
|
|
|
|
|
|
installQty: null,
|
|
|
|
|
|
|
|
returnQty: null,
|
|
|
|
|
|
|
|
activityStatusId: statusLookup.id,
|
|
|
|
|
|
|
|
activityStatus: statusLookup,
|
|
|
|
|
|
|
|
comment: '',
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else if (widget.provider.sparePartHelperModel == null ||
|
|
|
|
|
|
|
|
widget.provider.sparePartHelperModel?.id != widget.model?.id) {
|
|
|
|
widget.provider.sparePartHelperModel = SparePartHelperModel(
|
|
|
|
widget.provider.sparePartHelperModel = SparePartHelperModel(
|
|
|
|
id: widget.model?.id ?? 0,
|
|
|
|
id: widget.model?.id ?? 0,
|
|
|
|
workOrderId: widget.model?.workOrderId ?? widget.provider.currentWorkOrder?.data?.requestId,
|
|
|
|
workOrderId: widget.fromCm ? widget.model?.workOrderId ?? widget.provider.currentWorkOrder?.data?.requestId : null,
|
|
|
|
|
|
|
|
pmVisitId: !widget.fromCm ? widget.model?.pmVisitId : null,
|
|
|
|
sparePartAttachments: widget.model?.sparePartAttachments ?? [],
|
|
|
|
sparePartAttachments: widget.model?.sparePartAttachments ?? [],
|
|
|
|
sparePart: widget.model?.sparePart ?? SparePart(),
|
|
|
|
sparePart: widget.model?.sparePart ?? SparePart(),
|
|
|
|
quantity: widget.model?.quantity,
|
|
|
|
quantity: widget.model?.quantity,
|
|
|
|
@ -81,83 +95,96 @@ class _SparePartRequestState extends State<SparePartRequest> with TickerProvider
|
|
|
|
activityStatus: widget.model?.activityStatus ?? statusLookup,
|
|
|
|
activityStatus: widget.model?.activityStatus ?? statusLookup,
|
|
|
|
comment: widget.model?.comment ?? '',
|
|
|
|
comment: widget.model?.comment ?? '',
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
_partQtyController.text = widget.provider.sparePartHelperModel?.quantity != null ? widget.provider.sparePartHelperModel!.quantity!.round().toString() : '';
|
|
|
|
|
|
|
|
_installQtyController.text = widget.provider.sparePartHelperModel?.installQty != null ? widget.provider.sparePartHelperModel!.installQty!.round().toString() : '';
|
|
|
|
|
|
|
|
_returnQtyController.text = widget.provider.sparePartHelperModel?.returnQty != null ? widget.provider.sparePartHelperModel!.returnQty!.round().toString() : '';
|
|
|
|
|
|
|
|
_oracleNoController.text = widget.provider.sparePartHelperModel?.sparePart?.oracleCode != null ? widget.provider.sparePartHelperModel!.sparePart!.oracleCode! : '';
|
|
|
|
|
|
|
|
_descriptionController.text = widget.provider.sparePartHelperModel?.comment != null ? widget.provider.sparePartHelperModel!.comment! : '';
|
|
|
|
|
|
|
|
activityStatus = widget.provider.sparePartHelperModel?.activityStatus?.value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scheduleMicrotask(() async {
|
|
|
|
|
|
|
|
_isLoading = true;
|
|
|
|
|
|
|
|
attachments = widget.provider.sparePartHelperModel?.sparePartAttachments?.map((e) => GenericAttachmentModel(id: e.id!, name: e.name ?? '')).toList() ?? [];
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
_spareParts = await _partsProvider!.getPartsListByDisplayName(assetId: widget.provider.currentWorkOrder?.data?.asset?.id);
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log('Initialized SparePartHelperModel: ${widget.provider.sparePartHelperModel?.toJson()}');
|
|
|
|
void restValues({required CMDetailProvider requestDetailProvider}) {
|
|
|
|
|
|
|
|
requestDetailProvider.sparePartHelperModel = SparePartHelperModel(id: 0, workOrderId: widget.provider.currentWorkOrder?.data?.requestId, sparePartAttachments: []);
|
|
|
|
|
|
|
|
_partQtyController.clear();
|
|
|
|
|
|
|
|
_installQtyController.clear();
|
|
|
|
|
|
|
|
_returnQtyController.clear();
|
|
|
|
|
|
|
|
_oracleNoController.clear();
|
|
|
|
|
|
|
|
_descriptionController.clear();
|
|
|
|
|
|
|
|
attachments = [];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> _loadInitialData() async {
|
|
|
|
void dispose() {
|
|
|
|
setState(() => _isLoading = true);
|
|
|
|
_partQtyController.dispose();
|
|
|
|
_spareParts = await _partsProvider!.getPartsListByDisplayName(
|
|
|
|
_installQtyController.dispose();
|
|
|
|
assetId: widget.fromCm
|
|
|
|
_returnQtyController.dispose();
|
|
|
|
? widget.provider.currentWorkOrder?.data?.asset?.id
|
|
|
|
_oracleNoController.dispose();
|
|
|
|
: widget.provider.planPreventiveVisit.asset.id,
|
|
|
|
_descriptionController.dispose();
|
|
|
|
);
|
|
|
|
super.dispose();
|
|
|
|
final bool isNewActivity = widget.model?.id == null || widget.model?.id == 0;
|
|
|
|
|
|
|
|
_attachments = [];
|
|
|
|
|
|
|
|
if (!isNewActivity) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
final attachmentsList = widget.model?.sparePartAttachments;
|
|
|
|
|
|
|
|
if (attachmentsList != null && attachmentsList.isNotEmpty) {
|
|
|
|
|
|
|
|
for (var item in attachmentsList) {
|
|
|
|
|
|
|
|
_attachments.add(GenericAttachmentModel(
|
|
|
|
|
|
|
|
id: item.id ?? 0,
|
|
|
|
|
|
|
|
name: item.name ?? '',
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
log('Loaded ${_attachments.length} attachments for existing activity');
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
log('Error loading attachments: $e');
|
|
|
|
|
|
|
|
_attachments = [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log('New activity - starting with empty attachments');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool _isLocalUrl(String url) {
|
|
|
|
if (mounted) setState(() => _isLoading = false);
|
|
|
|
if (url.isEmpty != false) return false;
|
|
|
|
|
|
|
|
return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\');
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
bool showInstallAndReturnQty = activityStatus == 3 || activityStatus == 4;
|
|
|
|
|
|
|
|
return Scaffold(
|
|
|
|
return Scaffold(
|
|
|
|
key: _scaffoldKey,
|
|
|
|
|
|
|
|
appBar: DefaultAppBar(title: context.translation.sparePartRequest),
|
|
|
|
appBar: DefaultAppBar(title: context.translation.sparePartRequest),
|
|
|
|
body: Consumer<CMDetailProvider>(builder: (context, CMDetailProvider requestDetailProvider, child) {
|
|
|
|
body: ListenableBuilder(
|
|
|
|
|
|
|
|
listenable: widget.provider,
|
|
|
|
|
|
|
|
builder: (context, _) {
|
|
|
|
|
|
|
|
final provider = widget.provider;
|
|
|
|
|
|
|
|
final model = provider.sparePartHelperModel;
|
|
|
|
|
|
|
|
final bool showInstallAndReturnQty = (model?.activityStatus?.value == 3 || model?.activityStatus?.value == 4);
|
|
|
|
|
|
|
|
final bool isReadOnly = provider.isReadOnlyRequest ?? false;
|
|
|
|
|
|
|
|
|
|
|
|
return Form(
|
|
|
|
return Form(
|
|
|
|
key: _formKey,
|
|
|
|
key: _formKey,
|
|
|
|
child: Column(
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
children: [
|
|
|
|
SingleChildScrollView(
|
|
|
|
Expanded(
|
|
|
|
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
child: IgnorePointer(
|
|
|
|
child: IgnorePointer(
|
|
|
|
ignoring: widget.provider.isReadOnlyRequest,
|
|
|
|
ignoring: isReadOnly,
|
|
|
|
child: Column(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
children: [
|
|
|
|
children: [
|
|
|
|
Column(
|
|
|
|
_buildFormContent(
|
|
|
|
|
|
|
|
context,
|
|
|
|
|
|
|
|
model,
|
|
|
|
|
|
|
|
provider,
|
|
|
|
|
|
|
|
showInstallAndReturnQty,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
if (!isReadOnly) _buildSubmitButton(context, provider),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Widget _buildFormContent(
|
|
|
|
|
|
|
|
BuildContext context,
|
|
|
|
|
|
|
|
SparePartHelperModel? model,
|
|
|
|
|
|
|
|
dynamic provider,
|
|
|
|
|
|
|
|
bool showInstallAndReturnQty,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
return Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
children: [
|
|
|
|
children: [
|
|
|
|
context.translation.sparePartDetails.heading5(context).custom(color: AppColor.textStyleColor(context)),
|
|
|
|
context.translation.sparePartDetails.heading5(context).custom(color: AppColor.textStyleColor(context)),
|
|
|
|
// 12.height,
|
|
|
|
|
|
|
|
// SingleItemDropDownMenu<Lookup, ActivityStatusProvider>(
|
|
|
|
|
|
|
|
// context: context,
|
|
|
|
|
|
|
|
// height: 56.toScreenHeight,
|
|
|
|
|
|
|
|
// title: context.translation.activityStatus,
|
|
|
|
|
|
|
|
// showShadow: false,
|
|
|
|
|
|
|
|
// backgroundColor: AppColor.neutral100,
|
|
|
|
|
|
|
|
// initialValue: requestDetailProvider.sparePartHelperModel?.activityStatus,
|
|
|
|
|
|
|
|
// onSelect: (status) {
|
|
|
|
|
|
|
|
// requestDetailProvider.sparePartHelperModel?.activityStatus = status;
|
|
|
|
|
|
|
|
// },
|
|
|
|
|
|
|
|
// ),
|
|
|
|
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
SingleItemDropDownMenu<SparePart, NullableLoadingProvider>(
|
|
|
|
SingleItemDropDownMenu<SparePart, NullableLoadingProvider>(
|
|
|
|
context: context,
|
|
|
|
context: context,
|
|
|
|
@ -166,206 +193,211 @@ class _SparePartRequestState extends State<SparePartRequest> with TickerProvider
|
|
|
|
showShadow: false,
|
|
|
|
showShadow: false,
|
|
|
|
showAsBottomSheet: true,
|
|
|
|
showAsBottomSheet: true,
|
|
|
|
loading: _isLoading,
|
|
|
|
loading: _isLoading,
|
|
|
|
initialValue: requestDetailProvider.sparePartHelperModel?.sparePart,
|
|
|
|
initialValue: model?.sparePart,
|
|
|
|
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
|
|
|
|
onSelect: (part) {
|
|
|
|
onSelect: (part) async {
|
|
|
|
|
|
|
|
if (part != null) {
|
|
|
|
if (part != null) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.sparePart = part;
|
|
|
|
model?.sparePart = part;
|
|
|
|
_oracleNoController.text = part.oracleCode ?? '';
|
|
|
|
provider.updateSparePartHelperModel(model);
|
|
|
|
requestDetailProvider.updateSparePartHelperModel(widget.provider.sparePartHelperModel);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
if (model?.sparePart?.id != null) ...[
|
|
|
|
if (requestDetailProvider.sparePartHelperModel?.sparePart?.id != null) ...[
|
|
|
|
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
SizedBox(
|
|
|
|
SizedBox(
|
|
|
|
height: 30.toScreenHeight,
|
|
|
|
height: 30.toScreenHeight,
|
|
|
|
child: Text('View Parts Availability',
|
|
|
|
child: Text(
|
|
|
|
|
|
|
|
'View Parts Availability',
|
|
|
|
style: TextStyle(
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 12.toScreenWidth,
|
|
|
|
fontSize: 12.toScreenWidth,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
fontStyle: FontStyle.normal,
|
|
|
|
|
|
|
|
color: AppColor.primary10,
|
|
|
|
color: AppColor.primary10,
|
|
|
|
decorationColor: AppColor.primary10,
|
|
|
|
decorationColor: AppColor.primary10,
|
|
|
|
decoration: TextDecoration.underline,
|
|
|
|
decoration: TextDecoration.underline,
|
|
|
|
)).onPress(
|
|
|
|
),
|
|
|
|
() async {
|
|
|
|
).onPress(() {
|
|
|
|
requestDetailProvider.getStoreAvailabilityById(partId: requestDetailProvider.sparePartHelperModel?.sparePart?.id);
|
|
|
|
provider.getStoreAvailabilityById(partId: model?.sparePart?.id);
|
|
|
|
showModalBottomSheet(
|
|
|
|
showModalBottomSheet(
|
|
|
|
context: context,
|
|
|
|
context: context,
|
|
|
|
useSafeArea: true,
|
|
|
|
useSafeArea: true,
|
|
|
|
isScrollControlled: true,
|
|
|
|
isScrollControlled: true,
|
|
|
|
isDismissible: true,
|
|
|
|
isDismissible: true,
|
|
|
|
backgroundColor: AppColor.bottomSheetColor(context),
|
|
|
|
backgroundColor: AppColor.bottomSheetColor(context),
|
|
|
|
builder: (BuildContext context) => PartDetailBottomSheetSheet().bottomSheetContainer(context),
|
|
|
|
builder: (ctx) => PartDetailBottomSheetSheet(provider: provider).bottomSheetContainer(ctx),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
AppTextFormField(
|
|
|
|
AppTextFormField(
|
|
|
|
controller: _partQtyController,
|
|
|
|
key: ValueKey('quantity_${model?.quantity}'),
|
|
|
|
|
|
|
|
initialValue: model?.quantity?.round().toString() ?? '',
|
|
|
|
labelText: context.translation.quantity,
|
|
|
|
labelText: context.translation.quantity,
|
|
|
|
textInputType: TextInputType.number,
|
|
|
|
textInputType: TextInputType.number,
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
// showWithoutDecoration: true,
|
|
|
|
enable: model?.sparePart?.id != null,
|
|
|
|
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
|
|
|
|
|
|
|
|
enable: requestDetailProvider.sparePartHelperModel?.sparePart?.id != null,
|
|
|
|
|
|
|
|
validator: (value) => value == null || value.isEmpty
|
|
|
|
validator: (value) => value == null || value.isEmpty
|
|
|
|
? context.translation.requiredField
|
|
|
|
? context.translation.requiredField
|
|
|
|
: Validator.isNumeric(value)
|
|
|
|
: Validator.isNumeric(value)
|
|
|
|
? null
|
|
|
|
? null
|
|
|
|
: context.translation.onlyNumbers,
|
|
|
|
: context.translation.onlyNumbers,
|
|
|
|
onChange: (value) {
|
|
|
|
onChange: (value) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.quantity = num.tryParse(value ?? "");
|
|
|
|
model?.quantity = num.tryParse(value);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
onSaved: (text) {
|
|
|
|
onSaved: (text) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.quantity = num.tryParse(text ?? "");
|
|
|
|
model?.quantity = num.tryParse(text ?? '');
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
|
|
|
|
|
|
|
|
if (showInstallAndReturnQty) ...[
|
|
|
|
if (showInstallAndReturnQty) ...[
|
|
|
|
AppTextFormField(
|
|
|
|
AppTextFormField(
|
|
|
|
controller: _installQtyController,
|
|
|
|
key: ValueKey('installQty_${model?.installQty}'),
|
|
|
|
|
|
|
|
initialValue: model?.installQty?.round().toString() ?? '',
|
|
|
|
labelText: context.translation.installedQty,
|
|
|
|
labelText: context.translation.installedQty,
|
|
|
|
textInputType: const TextInputType.numberWithOptions(decimal: true),
|
|
|
|
textInputType: const TextInputType.numberWithOptions(decimal: true),
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
// showWithoutDecoration: true,
|
|
|
|
enable: model?.sparePart?.id != null,
|
|
|
|
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
|
|
|
|
|
|
|
|
enable: requestDetailProvider.sparePartHelperModel?.sparePart?.id != null,
|
|
|
|
|
|
|
|
validator: (value) => value == null || value.isEmpty
|
|
|
|
validator: (value) => value == null || value.isEmpty
|
|
|
|
? context.translation.requiredField
|
|
|
|
? context.translation.requiredField
|
|
|
|
: Validator.isNumeric(value)
|
|
|
|
: Validator.isNumeric(value)
|
|
|
|
? null
|
|
|
|
? null
|
|
|
|
: context.translation.onlyNumbers,
|
|
|
|
: context.translation.onlyNumbers,
|
|
|
|
onChange: (value) {
|
|
|
|
onChange: (value) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.installQty = num.tryParse(value ?? "");
|
|
|
|
model?.installQty = num.tryParse(value);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
onSaved: (text) {
|
|
|
|
onSaved: (text) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.installQty = num.tryParse(text ?? "");
|
|
|
|
model?.installQty = num.tryParse(text ?? '');
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
AppTextFormField(
|
|
|
|
AppTextFormField(
|
|
|
|
controller: _returnQtyController,
|
|
|
|
key: ValueKey('returnQty_${model?.returnQty}'),
|
|
|
|
|
|
|
|
initialValue: model?.returnQty?.round().toString() ?? '',
|
|
|
|
labelText: context.translation.returnQty,
|
|
|
|
labelText: context.translation.returnQty,
|
|
|
|
textInputType: TextInputType.number,
|
|
|
|
textInputType: TextInputType.number,
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
// showWithoutDecoration: true,
|
|
|
|
enable: model?.sparePart?.id != null,
|
|
|
|
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
|
|
|
|
|
|
|
|
enable: requestDetailProvider.sparePartHelperModel?.sparePart?.id != null,
|
|
|
|
|
|
|
|
validator: (value) => value == null || value.isEmpty
|
|
|
|
validator: (value) => value == null || value.isEmpty
|
|
|
|
? context.translation.requiredField
|
|
|
|
? context.translation.requiredField
|
|
|
|
: Validator.isNumeric(value)
|
|
|
|
: Validator.isNumeric(value)
|
|
|
|
? null
|
|
|
|
? null
|
|
|
|
: context.translation.onlyNumbers,
|
|
|
|
: context.translation.onlyNumbers,
|
|
|
|
onChange: (value) {
|
|
|
|
onChange: (value) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.returnQty = num.tryParse(value ?? "");
|
|
|
|
model?.returnQty = num.tryParse(value);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
onSaved: (text) {
|
|
|
|
onSaved: (text) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.returnQty = num.tryParse(text ?? "");
|
|
|
|
model?.returnQty = num.tryParse(text ?? '');
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
],
|
|
|
|
],
|
|
|
|
AppTextFormField(
|
|
|
|
AppTextFormField(
|
|
|
|
|
|
|
|
key: ValueKey('oracle_${model?.sparePart?.oracleCode}'),
|
|
|
|
|
|
|
|
initialValue: model?.sparePart?.oracleCode ?? '',
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
controller: _oracleNoController,
|
|
|
|
|
|
|
|
labelText: context.translation.oracleNo,
|
|
|
|
labelText: context.translation.oracleNo,
|
|
|
|
textInputType: TextInputType.number,
|
|
|
|
textInputType: TextInputType.number,
|
|
|
|
// showWithoutDecoration: true,
|
|
|
|
|
|
|
|
backgroundColor: AppColor.disableFieldBackground(context),
|
|
|
|
backgroundColor: AppColor.disableFieldBackground(context),
|
|
|
|
enable: false,
|
|
|
|
enable: false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
AppTextFormField(
|
|
|
|
AppTextFormField(
|
|
|
|
initialValue: requestDetailProvider.sparePartHelperModel?.comment,
|
|
|
|
key: ValueKey('comment_${model?.comment}'),
|
|
|
|
|
|
|
|
initialValue: model?.comment ?? '',
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
labelStyle: AppTextStyles.textFieldLabelStyle,
|
|
|
|
controller: _descriptionController,
|
|
|
|
|
|
|
|
showSpeechToText: true,
|
|
|
|
showSpeechToText: true,
|
|
|
|
// backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
|
|
|
|
|
|
|
|
labelText: "Engineer Comment",
|
|
|
|
labelText: "Engineer Comment",
|
|
|
|
alignLabelWithHint: true,
|
|
|
|
alignLabelWithHint: true,
|
|
|
|
// showWithoutDecoration: true,
|
|
|
|
|
|
|
|
textInputType: TextInputType.multiline,
|
|
|
|
textInputType: TextInputType.multiline,
|
|
|
|
onChange: (value) {
|
|
|
|
onChange: (value) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.comment = value;
|
|
|
|
model?.comment = value;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
onSaved: (value) {
|
|
|
|
onSaved: (value) {
|
|
|
|
requestDetailProvider.sparePartHelperModel?.comment = value;
|
|
|
|
model?.comment = value;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
12.height,
|
|
|
|
12.height,
|
|
|
|
AttachmentPicker(
|
|
|
|
AttachmentPicker(
|
|
|
|
label: context.translation.attachQuotation,
|
|
|
|
label: context.translation.attachQuotation,
|
|
|
|
attachment: attachments,
|
|
|
|
attachment: _attachments,
|
|
|
|
buttonIcon: 'quotation_icon'.toSvgAsset(color: AppColor.primary10),
|
|
|
|
buttonIcon: 'quotation_icon'.toSvgAsset(color: AppColor.primary10),
|
|
|
|
buttonColor: AppColor.primary10,
|
|
|
|
buttonColor: AppColor.primary10,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
).toShadowContainer(context),
|
|
|
|
).toShadowContainer(context);
|
|
|
|
8.height,
|
|
|
|
}
|
|
|
|
],
|
|
|
|
|
|
|
|
),
|
|
|
|
Widget _buildSubmitButton(BuildContext context, dynamic provider) {
|
|
|
|
),
|
|
|
|
return FooterActionButton.footerContainer(
|
|
|
|
).expanded,
|
|
|
|
|
|
|
|
if (!requestDetailProvider.isReadOnlyRequest)
|
|
|
|
|
|
|
|
FooterActionButton.footerContainer(
|
|
|
|
|
|
|
|
context: context,
|
|
|
|
context: context,
|
|
|
|
child: AppFilledButton(
|
|
|
|
child: AppFilledButton(
|
|
|
|
label: widget.provider.sparePartHelperModel?.id == 0 ? context.translation.addSparePartActivity : context.translation.updateSparePartActivity,
|
|
|
|
label: provider.sparePartHelperModel?.id == 0 ? context.translation.addSparePartActivity : context.translation.updateSparePartActivity,
|
|
|
|
buttonColor: AppColor.green70,
|
|
|
|
buttonColor: AppColor.green70,
|
|
|
|
onPressed: () {
|
|
|
|
onPressed: () => _onSubmit(provider),
|
|
|
|
_onSubmit(requestDetailProvider: requestDetailProvider);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
)),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_onSubmit({required CMDetailProvider requestDetailProvider}) async {
|
|
|
|
Future<void> _onSubmit(dynamic provider) async {
|
|
|
|
if (requestDetailProvider.sparePartHelperModel?.sparePart?.id == null) {
|
|
|
|
final model = provider.sparePartHelperModel;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (model?.sparePart?.id == null) {
|
|
|
|
"Please select spare part".showToast;
|
|
|
|
"Please select spare part".showToast;
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
} else if (requestDetailProvider.sparePartHelperModel?.quantity == null) {
|
|
|
|
} else if (model?.quantity == null) {
|
|
|
|
"Please enter quantity".showToast;
|
|
|
|
"Please enter quantity".showToast;
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
requestDetailProvider.sparePartHelperModel?.sparePartAttachments?.clear();
|
|
|
|
model?.sparePartAttachments?.clear();
|
|
|
|
|
|
|
|
for (var item in _attachments) {
|
|
|
|
for (var item in attachments) {
|
|
|
|
|
|
|
|
String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
|
|
|
|
String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
|
|
|
|
requestDetailProvider.sparePartHelperModel?.sparePartAttachments?.add(
|
|
|
|
|
|
|
|
|
|
|
|
model?.sparePartAttachments?.add(
|
|
|
|
SparePartAttachments(id: item.id, name: fileName),
|
|
|
|
SparePartAttachments(id: item.id, name: fileName),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Show loading dialog
|
|
|
|
// Show loading
|
|
|
|
showDialog(
|
|
|
|
showDialog(
|
|
|
|
context: context,
|
|
|
|
context: context,
|
|
|
|
barrierDismissible: false,
|
|
|
|
barrierDismissible: false,
|
|
|
|
builder: (context) => const AppLazyLoading(),
|
|
|
|
builder: (context) => const AppLazyLoading(),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Submit
|
|
|
|
bool success = false;
|
|
|
|
bool success = false;
|
|
|
|
if (widget.provider.sparePartHelperModel?.id == 0) {
|
|
|
|
if (model?.id == 0) {
|
|
|
|
success = await requestDetailProvider.createActivitySparePart();
|
|
|
|
success = await provider.createActivitySparePart();
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
success = await requestDetailProvider.updateActivitySparePart();
|
|
|
|
success = await provider.updateActivitySparePart();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mounted) Navigator.pop(context);
|
|
|
|
|
|
|
|
|
|
|
|
if (mounted) Navigator.pop(context); // Close loading
|
|
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
if (success) {
|
|
|
|
requestDetailProvider.getWorkOrderById(
|
|
|
|
if (widget.fromCm) {
|
|
|
|
id: requestDetailProvider.currentWorkOrder!.data!.requestId!,
|
|
|
|
provider.getWorkOrderById(
|
|
|
|
|
|
|
|
id: provider.currentWorkOrder!.data!.requestId!,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else{
|
|
|
|
|
|
|
|
provider.getPlanPreventiveVisitById(
|
|
|
|
|
|
|
|
int.tryParse(provider.planPreventiveVisit?.id ?? ''),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (mounted) Navigator.pop(context); // Close form
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (model?.id == 0) {
|
|
|
|
|
|
|
|
ServiceRequestBottomSheet.addAnotherSpareRequestBottomSheet(
|
|
|
|
|
|
|
|
context: context,
|
|
|
|
|
|
|
|
fromCm: widget.fromCm
|
|
|
|
);
|
|
|
|
);
|
|
|
|
if (mounted) Navigator.pop(context);
|
|
|
|
|
|
|
|
if (widget.provider.sparePartHelperModel?.id == 0) {
|
|
|
|
|
|
|
|
ServiceRequestBottomSheet.addAnotherSpareRequestBottomSheet(context: context);
|
|
|
|
|
|
|
|
const SizedBox().flushBar(
|
|
|
|
const SizedBox().flushBar(
|
|
|
|
context: context,
|
|
|
|
context: context,
|
|
|
|
title: context.translation.sparePartActivitySuccess,
|
|
|
|
title: context.translation.sparePartActivitySuccess,
|
|
|
|
@ -376,8 +408,14 @@ class _SparePartRequestState extends State<SparePartRequest> with TickerProvider
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Bottom Sheet for showing parts availability
|
|
|
|
class PartDetailBottomSheetSheet extends StatelessWidget {
|
|
|
|
class PartDetailBottomSheetSheet extends StatelessWidget {
|
|
|
|
PartDetailBottomSheetSheet({Key? key}) : super(key: key);
|
|
|
|
final dynamic provider;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const PartDetailBottomSheetSheet({
|
|
|
|
|
|
|
|
Key? key,
|
|
|
|
|
|
|
|
required this.provider,
|
|
|
|
|
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
@ -390,31 +428,61 @@ class PartDetailBottomSheetSheet extends StatelessWidget {
|
|
|
|
children: [
|
|
|
|
children: [
|
|
|
|
8.height,
|
|
|
|
8.height,
|
|
|
|
'Parts Availability'.heading6(context).paddingOnly(top: 12, start: 12),
|
|
|
|
'Parts Availability'.heading6(context).paddingOnly(top: 12, start: 12),
|
|
|
|
Consumer<CMDetailProvider>(builder: (context, requestDetailProvider, child) {
|
|
|
|
Expanded(
|
|
|
|
return requestDetailProvider.isLoading
|
|
|
|
child: ListenableBuilder(
|
|
|
|
? CircularProgressIndicator(color: AppColor.loadingColor(context)).center
|
|
|
|
listenable: provider,
|
|
|
|
: requestDetailProvider.storeAvailability.isEmpty
|
|
|
|
builder: (context, _) {
|
|
|
|
|
|
|
|
return provider.isLoading
|
|
|
|
|
|
|
|
? CircularProgressIndicator(
|
|
|
|
|
|
|
|
color: AppColor.loadingColor(context),
|
|
|
|
|
|
|
|
).center
|
|
|
|
|
|
|
|
: provider.storeAvailability.isEmpty
|
|
|
|
? const NoDataFound().center
|
|
|
|
? const NoDataFound().center
|
|
|
|
: ListView.separated(
|
|
|
|
: ListView.separated(
|
|
|
|
padding: const EdgeInsets.only(top: 12),
|
|
|
|
padding: const EdgeInsets.only(top: 12),
|
|
|
|
itemCount: requestDetailProvider.storeAvailability.length,
|
|
|
|
itemCount: provider.storeAvailability.length,
|
|
|
|
separatorBuilder: (czt, index) => 12.height,
|
|
|
|
separatorBuilder: (czt, index) => 12.height,
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
StoreAvailability model = requestDetailProvider.storeAvailability[index];
|
|
|
|
StoreAvailability model = provider.storeAvailability[index];
|
|
|
|
return partAvailableQuantityCard(context: context, model: model);
|
|
|
|
return _partAvailableQuantityCard(
|
|
|
|
|
|
|
|
context: context,
|
|
|
|
|
|
|
|
model: model,
|
|
|
|
|
|
|
|
);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}).expanded,
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Widget partAvailableQuantityCard({required StoreAvailability model, required BuildContext context}) {
|
|
|
|
Widget _partAvailableQuantityCard({
|
|
|
|
return Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
|
|
|
required StoreAvailability model,
|
|
|
|
InfoTextWidget(label: "Site Name:".addTranslation, value: model.siteName ?? ''),
|
|
|
|
required BuildContext context,
|
|
|
|
InfoTextWidget(label: "Store Name:".addTranslation, value: model.storeName ?? ''),
|
|
|
|
}) {
|
|
|
|
InfoTextWidget(label: "Available Quantity:".addTranslation, value: model.availablityQty != null ? model.availablityQty.toString() : ''),
|
|
|
|
return Column(
|
|
|
|
]).toShadowContainer(context, padding: 12, showShadow: false,);
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
|
|
|
children: [
|
|
|
|
|
|
|
|
InfoTextWidget(
|
|
|
|
|
|
|
|
label: "Site Name:".addTranslation,
|
|
|
|
|
|
|
|
value: model.siteName ?? '',
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
InfoTextWidget(
|
|
|
|
|
|
|
|
label: "Store Name:".addTranslation,
|
|
|
|
|
|
|
|
value: model.storeName ?? '',
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
InfoTextWidget(
|
|
|
|
|
|
|
|
label: "Available Quantity:".addTranslation,
|
|
|
|
|
|
|
|
value: model.availablityQty?.toString() ?? '',
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
).toShadowContainer(
|
|
|
|
|
|
|
|
context,
|
|
|
|
|
|
|
|
padding: 12,
|
|
|
|
|
|
|
|
showShadow: false,
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|