import 'package:fcs/domain/constants.dart'; import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/domain/entities/cargo_type.dart'; import 'package:fcs/domain/entities/carton_size.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/domain/entities/user.dart'; import 'package:fcs/domain/vo/delivery_address.dart'; import 'package:fcs/helpers/theme.dart'; import 'package:fcs/pages/carton/carton_package_table.dart'; import 'package:fcs/pages/carton_search/carton_search.dart'; import 'package:fcs/pages/carton_size/carton_size_list.dart'; import 'package:fcs/pages/delivery_address/model/delivery_address_model.dart'; import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart'; import 'package:fcs/pages/main/util.dart'; import 'package:fcs/pages/package/model/package_model.dart'; import 'package:fcs/pages/rates/model/shipment_rate_model.dart'; import 'package:fcs/pages/user_search/user_serach.dart'; import 'package:fcs/pages/widgets/defalut_delivery_address.dart'; import 'package:fcs/pages/widgets/delivery_address_selection.dart'; import 'package:fcs/pages/widgets/display_text.dart'; import 'package:fcs/pages/widgets/fcs_id_icon.dart'; import 'package:fcs/pages/widgets/length_picker.dart'; import 'package:fcs/pages/widgets/local_button.dart'; import 'package:fcs/pages/widgets/local_dropdown.dart'; import 'package:fcs/pages/widgets/local_radio_buttons.dart'; import 'package:fcs/pages/widgets/local_text.dart'; import 'package:fcs/pages/widgets/local_title.dart'; import 'package:fcs/pages/widgets/progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons/flutter_icons.dart'; import 'package:provider/provider.dart'; import 'cargo_type_addtion.dart'; import 'carton_cargo_table.dart'; import 'carton_row.dart'; import 'model/carton_model.dart'; import '../carton_size/model/carton_size_model.dart'; import 'package_carton_editor.dart'; import 'widgets.dart'; class CartonEditor extends StatefulWidget { final Carton box; CartonEditor({this.box}); @override _CartonEditorState createState() => _CartonEditorState(); } class _CartonEditorState extends State { TextEditingController _widthController = new TextEditingController(); TextEditingController _heightController = new TextEditingController(); TextEditingController _lengthController = new TextEditingController(); List _deliveryAddresses = []; DeliveryAddress _deliveryAddress = new DeliveryAddress(); List _cargoTypes = []; Carton _carton; bool _isLoading = false; bool _isNew; User _user; String _selectedCartonType; double volumetricRatio = 0; double shipmentWeight = 0; FcsShipment _fcsShipment; List _fcsShipments; List _cartons = []; CartonSize selectedCatonSize; //for mix carton List _mixCartons = []; String _selectedMixBoxType; //for carton from cargos User consignee; User sender; List _cartonsForCargos = []; @override void initState() { super.initState(); //for shipment weight volumetricRatio = Provider.of(context, listen: false) .rate .volumetricRatio; _lengthController.addListener(_calShipmentWeight); _widthController.addListener(_calShipmentWeight); _heightController.addListener(_calShipmentWeight); if (widget.box != null) { _carton = widget.box; _deliveryAddress = _carton.deliveryAddress; _widthController.text = _carton.width.toString(); _heightController.text = _carton.height.toString(); _lengthController.text = _carton.length.toString(); _selectedCartonType = _carton.cartonType; _cargoTypes = List.from(_carton.cargoTypes); _isNew = false; _user = User( id: _carton.userID, fcsID: _carton.fcsID, name: _carton.userName); consignee = User( id: _carton.receiverID, fcsID: _carton.receiverFCSID, name: _carton.receiverName); sender = User( id: _carton.senderID, fcsID: _carton.senderID, name: _carton.senderName); _selectedMixBoxType = _carton.mixBoxType ?? ""; this._mixCartons = _carton.mixCartons == null ? [] : List.from(_carton.mixCartons); bool isMixBox = _carton.cartonType == carton_mix_box; bool isFromPackages = _carton.cartonType == carton_from_packages; if (isFromPackages) _loadPackages(); if (!isMixBox) { _getDeliverAddresses(); _getCartonSize(); } } else { _carton = Carton( cargoTypes: [], packages: [], ); _lengthController.text = "0"; _widthController.text = "0"; _heightController.text = "0"; _isNew = true; _selectedCartonType = carton_from_packages; _selectedMixBoxType = mix_delivery; _loadFcsShipments(); } } _loadFcsShipments() async { FcsShipmentModel fcsShipmentModel = Provider.of(context, listen: false); var fcsShipments = await fcsShipmentModel.getActiveFcsShipments(); var fcsShipment = fcsShipments .firstWhere((e) => e.id == _carton.fcsShipmentID, orElse: () => null); setState(() { _fcsShipments = fcsShipments; _fcsShipment = fcsShipment; }); } _loadPackages() async { if (_user == null) return; PackageModel packageModel = Provider.of(context, listen: false); List packages = await packageModel.getPackages( _user.id, [package_processed_status, package_packed_status]); if (_isNew) { String prevCompare; packages.forEach((p) { String compare = (p.deliveryAddress?.fullName ?? "") + (p.deliveryAddress?.phoneNumber ?? ""); if (prevCompare != null && compare == prevCompare) { p.isChecked = true; } else { p.isChecked = false; } if (prevCompare == null) { p.isChecked = true; prevCompare = compare; } }); } else { packages.forEach((p) { if (_carton.packages.contains(p)) { p.isChecked = _carton.packages.firstWhere((cp) => cp == p).isChecked; } else { p.isChecked = false; } }); } setState(() { _carton.packages = packages; }); // _populateDeliveryAddress(); } // _populateDeliveryAddress() { // if (_carton.packages == null) return; // var d = _carton.packages // .firstWhere((p) => p.isChecked && p.deliveryAddress != null, // orElse: () => null) // ?.deliveryAddress; // setState(() { // _deliveryAddress = d; // }); // } _calShipmentWeight() { double l = double.parse(_lengthController.text, (s) => 0); double w = double.parse(_widthController.text, (s) => 0); double h = double.parse(_heightController.text, (s) => 0); setState(() { shipmentWeight = l * w * h / volumetricRatio; }); } _getDeliverAddresses() async { var addressModel = Provider.of(context, listen: false); bool isFromPackages = _carton.cartonType == carton_from_packages; this._deliveryAddresses = isFromPackages ? await addressModel.getDeliveryAddresses(_carton.userID) : await addressModel.getDeliveryAddresses(_carton.receiverID); } _getCartonSize() { var cartonSizeModel = Provider.of(context, listen: false); cartonSizeModel.cartonSizes.forEach((c) { if (c.length == _carton.length && c.width == _carton.width && c.height == _carton.height) { selectedCatonSize = CartonSize( id: c.id, name: c.name, length: c.length, width: c.width, height: c.height); } }); } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { var boxModel = Provider.of(context); bool isFromPackages = _selectedCartonType == carton_from_packages; bool isFromCargos = _selectedCartonType == carton_from_cargos; bool isMixBox = _selectedCartonType == carton_mix_box; final shipmentBox = DisplayText( text: _carton.fcsShipmentNumber, labelTextKey: "box.fcs_shipment_num", iconData: Ionicons.ios_airplane, ); var fcsShipmentsBox = Container( padding: EdgeInsets.only(top: 10), child: LocalDropdown( callback: (v) { setState(() { _fcsShipment = v; }); }, labelKey: "shipment.pack.fcs.shipment", iconData: Ionicons.ios_airplane, display: (u) => u.shipmentNumber, selectedValue: _fcsShipment, values: _fcsShipments, )); final fcsIDBox = Container( padding: EdgeInsets.only(top: 10), child: Row( children: [ Expanded( child: DisplayText( text: _user?.fcsID ?? "", labelTextKey: "box.fcs.id", icon: FcsIDIcon(), )), _isNew ? IconButton( icon: Icon(Icons.search, color: primaryColor), onPressed: () => searchUser(context, onUserSelect: (u) { setState(() { this._user = u; _loadPackages(); }); }, popPage: true)) : Container(), ], )); final namebox = DisplayText( text: _user?.name ?? "", labelTextKey: "box.name", iconData: Icons.person, ); final createBtn = LocalButton( textKey: "box.complete.packaging", callBack: () { Navigator.pop(context); }, ); final saveBtn = LocalButton( textKey: "box.cargo.save.btn", callBack: _save, ); final cartonTypeBox = LocalRadioButtons( readOnly: !_isNew, values: boxModel.cartonTypes, selectedValue: _selectedCartonType, callback: (v) { setState(() { _selectedCartonType = v; }); }); final cartonTitleBox = Container( child: LocalTitle( textKey: "boxes.title", trailing: IconButton( icon: Icon( Icons.add_circle, color: primaryColor, ), onPressed: _addCarton), ), ); final mixTypeBox = Container( padding: EdgeInsets.only(top: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.only(left: 5), child: LocalText( context, "box.mix_type", color: primaryColor, fontSize: 15, fontWeight: FontWeight.bold, )), Row( children: boxModel.mixBoxTypes.map((e) { return Row( children: [ Radio( value: e, groupValue: _selectedMixBoxType, activeColor: primaryColor, onChanged: (v) { setState(() { _selectedMixBoxType = v; }); }, ), Text(e), ], ); }).toList(), ), ], ), ); final mixTypeDisplayBox = Container( padding: EdgeInsets.only(top: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.only(left: 5), child: LocalText( context, "box.mix_type", color: primaryColor, fontSize: 15, fontWeight: FontWeight.bold, )), Row( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Icon( Icons.check, color: primaryColor, ), ), Text(_selectedMixBoxType ?? "") ], ) ], ), ); final mixcartonTitleBox = Container( child: LocalTitle( textKey: "box.mix_caton_title", trailing: IconButton( icon: Icon( Icons.add_circle, color: primaryColor, ), onPressed: () async { searchCarton(context, callbackCartonSelect: (c) { _addMixCarton(c); }); }, ), ), ); final cargoTableTitleBox = LocalTitle( textKey: "box.cargo.type", trailing: IconButton( icon: Icon( Icons.add_circle, color: primaryColor, ), onPressed: () async { List cargos = await Navigator.push>( context, CupertinoPageRoute(builder: (context) => CargoTypeAddition())); if (cargos == null) return; setState(() { _cargoTypes.clear(); _cargoTypes.addAll(cargos); }); }), ); final cargoTableBox = CargoTable( isNew: _isNew, cargoTypes: _cargoTypes, onAdd: (c) => _addCargo(c), onRemove: (c) => _removeCargo(c), ); final lengthBox = LengthPicker( controller: _lengthController, lableKey: "box.length", isReadOnly: true, ); final widthBox = LengthPicker( controller: _widthController, lableKey: "box.width", isReadOnly: true, ); final heightBox = LengthPicker( controller: _heightController, lableKey: "box.height", isReadOnly: true, ); final dimBox = Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(right: 8.0), child: Icon(FontAwesome.arrow_circle_right, color: primaryColor), ), SizedBox(child: lengthBox, width: 80), SizedBox(child: widthBox, width: 80), SizedBox(child: heightBox, width: 80), ], ); final createMixCarton = LocalButton( textKey: _isNew ? "box.mix_carton_btn" : "btn.save", callBack: _creatMixCarton, ); final consigneefcsIDBox = Row( children: [ Expanded( child: DisplayText( text: consignee != null ? consignee.fcsID : "", labelTextKey: "processing.fcs.id", icon: FcsIDIcon(), )), IconButton( icon: Icon(Icons.search, color: primaryColor), onPressed: () => searchUser(context, onUserSelect: (u) { setState(() { this.consignee = u; }); }, popPage: true)), ], ); final consigneeNameBox = DisplayText( text: consignee != null ? consignee.name : "", labelTextKey: "processing.consignee.name", maxLines: 2, iconData: Icons.person, ); final consigneeBox = Container( child: Column( children: [ consigneefcsIDBox, consigneeNameBox, ], ), ); final shipperIDBox = Row( children: [ Expanded( child: DisplayText( text: sender != null ? sender.fcsID : "", labelTextKey: "processing.fcs.id", icon: FcsIDIcon(), )), IconButton( icon: Icon(Icons.search, color: primaryColor), onPressed: () => searchUser(context, onUserSelect: (u) { setState(() { this.sender = u; }); }, popPage: true)), ], ); final shipperNamebox = DisplayText( text: sender != null ? sender.name : "", labelTextKey: "processing.shipper.name", maxLines: 2, iconData: Icons.person, ); final shipperBox = Container( child: Column( children: [ shipperIDBox, shipperNamebox, ], ), ); return LocalProgress( inAsyncCall: _isLoading, child: Scaffold( appBar: AppBar( centerTitle: true, leading: new IconButton( icon: new Icon(CupertinoIcons.back, color: primaryColor, size: 30), onPressed: () { if (isDataChanged()) { showConfirmDialog(context, "back.button_confirm", () { Navigator.of(context).pop(); }); } else { Navigator.of(context).pop(); } }, ), shadowColor: Colors.transparent, backgroundColor: Colors.white, title: LocalText( context, _isNew ? "box.info.title" : "box.edit.title", fontSize: 20, color: primaryColor, ), ), body: Padding( padding: const EdgeInsets.all(8.0), child: ListView( shrinkWrap: true, children: [ _isNew ? Container() : Center(child: getCartonNumberStatus(context, _carton)), LocalTitle(textKey: "box.type.title"), cartonTypeBox, LocalTitle(textKey: "box.shipment_info"), _isNew ? fcsShipmentsBox : shipmentBox, isMixBox ? _isNew ? mixTypeBox : mixTypeDisplayBox : Container(), ...(isMixBox ? [ mixcartonTitleBox, Column( children: _getMixCartons(context, this._mixCartons)), ] : [ isFromPackages ? fcsIDBox : Container(), isFromPackages ? namebox : Container(), isFromPackages ? CartonPackageTable( packages: _carton.packages, onSelect: (p, checked) { if (checked && _deliveryAddress != null && p.deliveryAddress?.id != _deliveryAddress.id) { return; } setState(() { p.isChecked = checked; }); // _populateDeliveryAddress(); }, ) : Container(), isFromCargos ? Container( padding: const EdgeInsets.only(top: 15), child: Row( children: [ Flexible(child: consigneeBox), Flexible(child: shipperBox) ], ), ) : Container(), _isNew ? cartonTitleBox : Container(), _isNew ? Column( children: _getCartons( context, isFromPackages ? this._cartons : this._cartonsForCargos)) : Container(), _isNew ? Container() : cargoTableTitleBox, _isNew ? Container() : cargoTableBox, _isNew ? Container() : LocalTitle(textKey: "box.dimension"), _isNew ? Container() : cartonSizeDropdown(), _isNew ? Container() : dimBox, _isNew ? Container() : LocalTitle(textKey: "box.delivery_address"), _isNew ? Container() : DefaultDeliveryAddress( deliveryAddress: _deliveryAddress, labelKey: "box.delivery_address", onTap: () async { DeliveryAddress d = await Navigator.push( context, CupertinoPageRoute( builder: (context) => DeliveryAddressSelection( deliveryAddress: _deliveryAddress, deliveryAddresses: _deliveryAddresses)), ); if (d == null) return; setState(() { _deliveryAddress = d; }); }), ]), SizedBox( height: 20, ), isFromPackages || isFromCargos ? _isNew ? createBtn : saveBtn : Container(), isMixBox ? createMixCarton : Container(), SizedBox( height: 20, ), ], ), ), ), ); } List _getCartons(BuildContext context, List cartons) { return cartons.asMap().entries.map((c) { return InkWell( onTap: () async { bool isFromPackages = _selectedCartonType == carton_from_packages; bool isFromCargos = _selectedCartonType == carton_from_cargos; if (isFromPackages) { _loadPackages(); c.value.packages = _carton.packages; Carton _c = await Navigator.push( context, CupertinoPageRoute( builder: (context) => PackageCartonEditor( carton: c.value, isNew: false, consignee: _user, )), ); if (_c == null) return; cartons.removeWhere((item) => item.id == _c.id); cartons.insert(c.key, _c); setState(() {}); } }, child: CartonRow( key: ValueKey(c.value.id), box: c.value, ), ); }).toList(); } List _getMixCartons(BuildContext context, List cartons) { return cartons.map((c) { return CartonRow( key: ValueKey(c.id), box: c, onRemove: (carton) { _removeMixCarton(carton); }, ); }).toList(); } Widget cartonSizeDropdown() { List _cartonSizes = Provider.of(context).getCartonSizes; return Padding( padding: const EdgeInsets.only(top: 10), child: Row( children: [ Padding( padding: const EdgeInsets.only(left: 0, right: 10), child: Icon(AntDesign.CodeSandbox, color: primaryColor), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(right: 18.0), child: LocalText( context, "box.carton_size", color: Colors.black54, fontSize: 16, ), ), DropdownButton( isDense: true, value: selectedCatonSize, style: TextStyle(color: Colors.black, fontSize: 14), underline: Container( height: 1, color: Colors.grey, ), onChanged: (CartonSize newValue) { setState(() { if (newValue.name == MANAGE_CARTONSIZE) { selectedCatonSize = null; _manageCartonSize(); return; } selectedCatonSize = newValue; _widthController.text = selectedCatonSize.width.toString(); _heightController.text = selectedCatonSize.height.toString(); _lengthController.text = selectedCatonSize.length.toString(); }); }, isExpanded: true, items: _cartonSizes .map>((CartonSize value) { return DropdownMenuItem( value: value, child: Text(value.name ?? "", overflow: TextOverflow.ellipsis, style: TextStyle( color: value.name == MANAGE_CARTONSIZE ? secondaryColor : primaryColor)), ); }).toList(), ), ], ), ), ], ), ); } _manageCartonSize() { Navigator.push( context, CupertinoPageRoute(builder: (context) => CartonSizeList()), ); } _addCargo(CargoType cargo) { if (cargo == null) return; setState(() { _cargoTypes.remove(cargo); _cargoTypes.add(cargo); }); } _removeCargo(CargoType cargo) { setState(() { _cargoTypes.remove(cargo); }); } _addCarton() async { bool isFromPackages = _selectedCartonType == carton_from_packages; bool isFromCargos = _selectedCartonType == carton_from_cargos; if (_fcsShipment == null && _isNew) { showMsgDialog(context, "Error", "Please select FCS shipment"); return; } if (_user == null && isFromPackages) { showMsgDialog(context, "Error", "Please select FCS ID"); return; } if (consignee == null && isFromCargos) { showMsgDialog(context, "Error", "Please select consignee's FCS ID"); return; } if (sender == null && isFromCargos) { showMsgDialog(context, "Error", "Please select sender's FCS ID"); return; } double l = double.parse(_lengthController.text, (s) => 0); double w = double.parse(_widthController.text, (s) => 0); double h = double.parse(_heightController.text, (s) => 0); Carton carton = Carton(); carton.id = _carton.id; carton.cartonType = _selectedCartonType; carton.fcsShipmentID = _isNew ? _fcsShipment.id : _carton.fcsShipmentID; if (isFromPackages) { carton.userID = _user?.id; carton.fcsID = _user?.fcsID; carton.userName = _user?.name; carton.packages = _carton.packages.where((e) => e.isChecked).toList(); } if (isFromCargos) { carton.receiverID = consignee?.id; carton.receiverFCSID = consignee?.fcsID; carton.receiverName = consignee?.name; carton.senderID = sender?.id; carton.senderFCSID = sender?.fcsID; carton.senderName = sender?.name; } carton.cargoTypes = _carton.cargoTypes; carton.length = l; carton.width = w; carton.height = h; carton.deliveryAddress = _carton.deliveryAddress; try { Carton _c = await Navigator.push( context, CupertinoPageRoute( builder: (context) => PackageCartonEditor( carton: carton, isNew: _isNew, consignee: _user, )), ); if (_c == null) return; var cartonModel = Provider.of(context, listen: false); Carton _carton = await cartonModel.getCarton(_c.id); if (isFromPackages) { _cartons.add(_carton); } if (isFromCargos) { _cartonsForCargos.add(_carton); } setState(() {}); } catch (e) { showMsgDialog(context, "Error", e.toString()); } } _addMixCarton(Carton carton) { if (carton == null) return; if (this._mixCartons.any((c) => c.id == carton.id)) return; setState(() { this._mixCartons.add(carton); }); } _removeMixCarton(Carton carton) { setState(() { this._mixCartons.removeWhere((c) => c.id == carton.id); }); } _creatMixCarton() async { if (_fcsShipment == null && _isNew) { showMsgDialog(context, "Error", "Please select FCS shipment"); return; } if ((this._mixCartons?.length ?? 0) == 0) { showMsgDialog(context, "Error", "Expect at least one carton"); return; } Carton carton = Carton(); carton.id = _carton.id; carton.cartonType = _selectedCartonType; carton.fcsShipmentID = _isNew ? _fcsShipment.id : _carton.fcsShipmentID; carton.mixBoxType = _selectedMixBoxType; carton.mixCartons = this._mixCartons; setState(() { _isLoading = true; }); try { CartonModel cartonModel = Provider.of(context, listen: false); if (_isNew) { await cartonModel.createCarton(carton); } else { await cartonModel.updateCarton(carton); } Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { setState(() { _isLoading = false; }); } } _save() async { bool isFromPackages = _selectedCartonType == carton_from_packages; bool isFromCargos = _selectedCartonType == carton_from_cargos; if ((_cargoTypes?.length ?? 0) == 0 && (isFromPackages || isFromCargos)) { showMsgDialog(context, "Error", "Expect at least one cargo type"); return; } double l = double.parse(_lengthController.text, (s) => 0); double w = double.parse(_widthController.text, (s) => 0); double h = double.parse(_heightController.text, (s) => 0); if ((l <= 0 || w <= 0 || h <= 0) && (isFromPackages || isFromCargos)) { showMsgDialog(context, "Error", "Invalid dimension"); return; } if (_deliveryAddress == null && (isFromPackages || isFromCargos)) { showMsgDialog(context, "Error", "Invalid delivery address"); return; } Carton carton = Carton(); carton.id = _carton.id; carton.cartonType = _selectedCartonType; carton.fcsShipmentID = _isNew ? _fcsShipment.id : _carton.fcsShipmentID; if (isFromPackages) { carton.userID = _user?.id; carton.packages = _carton.packages.where((e) => e.isChecked).toList(); } if (isFromCargos) { carton.receiverID = consignee?.id; carton.senderID = sender?.id; } carton.cargoTypes = _cargoTypes; carton.length = l; carton.width = w; carton.height = h; carton.deliveryAddress = _deliveryAddress; setState(() { _isLoading = true; }); try { CartonModel cartonModel = Provider.of(context, listen: false); await cartonModel.updateCarton(carton); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { setState(() { _isLoading = false; }); } } isDataChanged() { if (_isNew) { return false; // return _fcsShipment != null || _user != null; } else { return false; } } }