diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index 94d7a24..a364a45 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -300,6 +300,7 @@ "box.min_caton.form.title":"Mix Carton", "box.mix_carton_btn":"Create mix carton", "box.mix_type":"Mix Box Types", + "box.selection":"Carton Selection", "Boxes End ================================================================":"", "Delivery Start ================================================================":"", diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 29ffefc..4bfea1f 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -300,6 +300,7 @@ "box.min_caton.form.title":"Mix Carton", "box.mix_carton_btn":"Create mix carton", "box.mix_type":"Mix Box Types", + "box.selection":"သေတ္တာ ရွေးချယ်ခြင်း", "Boxes End ================================================================":"", "Delivery Start ================================================================":"", diff --git a/lib/data/provider/carton_data_provider.dart b/lib/data/provider/carton_data_provider.dart index 332d1ff..54c4117 100644 --- a/lib/data/provider/carton_data_provider.dart +++ b/lib/data/provider/carton_data_provider.dart @@ -1,5 +1,8 @@ import 'dart:async'; +import 'dart:convert'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fcs/domain/constants.dart'; import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/helpers/api_helper.dart'; import 'package:fcs/helpers/firebase_helper.dart'; @@ -31,4 +34,32 @@ class CartonDataProvider { return await requestAPI("/cartons/deliver", "PUT", payload: carton.toMap(), token: await getToken()); } + + Future> searchCarton(String term) async { + if (term == null || term == '') return List(); + + // var bytes = utf8.encode(term); + // var base64Str = base64.encode(bytes); + // HtmlEscape htmlEscape = const HtmlEscape(); + // String escapeBuyer = htmlEscape.convert(base64Str); + + try { + String path = "/$cartons_collection"; + var querySnap = await Firestore.instance + .collection(path) + .where("carton_number", isEqualTo: term) + .where("carton_type", + whereIn: [carton_from_packages, carton_from_cargos]) + .where("status", isEqualTo: carton_packed_status) + .where("is_deleted", isEqualTo: false) + .orderBy("user_name") + .getDocuments(); + return querySnap.documents + .map((e) => Carton.fromMap(e.data, e.documentID)) + .toList(); + } catch (e) { + log.warning("carton error:" + e.toString()); + return null; + } + } } diff --git a/lib/data/services/carton_imp.dart b/lib/data/services/carton_imp.dart index 9b0e743..ba97084 100644 --- a/lib/data/services/carton_imp.dart +++ b/lib/data/services/carton_imp.dart @@ -33,4 +33,9 @@ class CartonServiceImp implements CartonService { Future deliver(Carton carton) { return cartonDataProvider.deliver(carton); } + + @override + Future> searchCarton(String term) { + return cartonDataProvider.searchCarton(term); + } } diff --git a/lib/data/services/carton_service.dart b/lib/data/services/carton_service.dart index d16cd6f..1137373 100644 --- a/lib/data/services/carton_service.dart +++ b/lib/data/services/carton_service.dart @@ -5,4 +5,5 @@ abstract class CartonService { Future updateCarton(Carton carton); Future deleteCarton(Carton carton); Future deliver(Carton carton); + Future> searchCarton(String term); } diff --git a/lib/domain/constants.dart b/lib/domain/constants.dart index 69ab4fc..9d1019e 100644 --- a/lib/domain/constants.dart +++ b/lib/domain/constants.dart @@ -78,6 +78,7 @@ const shipment_courier_dropoff = "Courier drop off"; //Carton types const carton_from_packages = "From packages"; +const carton_from_cargos="From cargos"; const carton_from_shipments = "From shipments"; const carton_mix_carton = "Mix carton"; const carton_small_bag = "Small bag"; diff --git a/lib/domain/entities/carton.dart b/lib/domain/entities/carton.dart index 3277475..20045c5 100644 --- a/lib/domain/entities/carton.dart +++ b/lib/domain/entities/carton.dart @@ -12,8 +12,10 @@ class Carton { String id; String shipmentID; String shipmentNumber; + String senderID; String senderFCSID; String senderName; + String receiverID; String receiverFCSID; String receiverName; String receiverAddress; @@ -38,7 +40,6 @@ class Carton { String mixCartonNumber; String cartonSizeID; String cartonSizeName; - String mixBoxType; int rate; int weight; @@ -56,6 +57,10 @@ class Carton { DeliveryAddress deliveryAddress; Shipment shipment; + //for mix box + String mixBoxType; + List mixCartons; + int get amount => rate != null && weight != null ? rate * weight : 0; String get packageNumber => @@ -126,8 +131,10 @@ class Carton { {this.id, this.shipmentID, this.shipmentNumber, + this.senderID, this.senderFCSID, this.senderName, + this.receiverID, this.receiverFCSID, this.receiverName, this.receiverNumber, @@ -164,13 +171,15 @@ class Carton { this.deliveryAddress, this.cartonSizeID, this.cartonSizeName, - this.mixBoxType}); + this.mixBoxType, + this.mixCartons}); Map toMap() { List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList(); List _packages = packages?.map((c) => c.toJson())?.toList(); + List _mixCartons = mixCartons?.map((c) => c.toJson())?.toList(); return { - "id": id, + 'id': id, 'fcs_shipment_id': fcsShipmentID, 'user_id': userID, 'cargo_types': _cargoTypes, @@ -181,6 +190,14 @@ class Carton { 'delivery_address': deliveryAddress?.toMap(), 'carton_type': cartonType, 'mix_carton_id': mixCartonID, + 'mix_box_type': mixBoxType, + 'mix_cartons': _mixCartons, + 'receiver_id': receiverID, + 'receiver_fcs_id': receiverFCSID, + 'receiver_name': receiverName, + 'sender_id': senderID, + 'sender_fcs_id': senderFCSID, + 'sender_name': senderName }; } @@ -192,30 +209,71 @@ class Carton { List>.from(map['cargo_types'] ?? []); var cargoTypes = cargoTypesMaps.map((e) => CargoType.fromMap(e, e["id"])).toList(); + var mixCartonsMaps = + List>.from(map['mix_cartons'] ?? []); + var _mixCartons = + mixCartonsMaps.map((e) => Carton.fromMap(e, e["id"])).toList(); + return Carton( - id: docID, - arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null, - shipmentID: map['shipment_id'], - shipmentNumber: map['shipment_number'], - receiverNumber: map['receiver_number'], - boxNumber: map['box_number'], - length: double.tryParse(map['length']?.toString()), - width: double.tryParse(map['width']?.toString()), - height: double.tryParse(map['height']?.toString()), - userName: map['user_name'], - fcsID: map['fcs_id'], - cartonType: map['carton_type'], - cartonNumber: map['carton_number'], - userID: map['user_id'], - fcsShipmentID: map['fcs_shipment_id'], - fcsShipmentNumber: map['fcs_shipment_number'], - isShipmentCarton: map['is_shipment_carton'], - mixCartonID: map['mix_carton_id'], - mixCartonNumber: map['mix_carton_number'], - status: map['status'], - packageIDs: List.from(map['package_ids'] ?? []), - deliveryAddress: _da, - cargoTypes: cargoTypes); + id: docID, + arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null, + shipmentID: map['shipment_id'], + shipmentNumber: map['shipment_number'], + receiverNumber: map['receiver_number'], + boxNumber: map['box_number'], + length: double.tryParse(map['length']?.toString()), + width: double.tryParse(map['width']?.toString()), + height: double.tryParse(map['height']?.toString()), + userName: map['user_name'], + fcsID: map['fcs_id'], + cartonType: map['carton_type'], + cartonNumber: map['carton_number'], + userID: map['user_id'], + fcsShipmentID: map['fcs_shipment_id'], + fcsShipmentNumber: map['fcs_shipment_number'], + isShipmentCarton: map['is_shipment_carton'], + mixCartonID: map['mix_carton_id'], + mixCartonNumber: map['mix_carton_number'], + status: map['status'], + packageIDs: List.from(map['package_ids'] ?? []), + deliveryAddress: _da, + cargoTypes: cargoTypes, + mixBoxType: map['mix_box_type'], + mixCartons: _mixCartons, + receiverID: map['receiver_id'], + receiverFCSID: map['receiver_fcs_id'], + receiverName: map['receiver_name'], + senderID: map['sender_id'], + senderFCSID: map['sender_fcs_id'], + senderName: map['sender_name'], + ); + } + + Map toJson() { + List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList(); + List _packages = packages?.map((c) => c.toJson())?.toList(); + List _mixCartons = mixCartons?.map((c) => c.toJson())?.toList(); + return { + 'id': id, + 'fcs_shipment_id': fcsShipmentID, + 'user_id': userID, + 'cargo_types': _cargoTypes, + 'packages': _packages, + 'length': length, + 'width': width, + 'height': height, + 'delivery_address': deliveryAddress?.toMap(), + 'carton_type': cartonType, + 'mix_carton_id': mixCartonID, + 'mix_box_type': mixBoxType, + 'mix_cartons': _mixCartons, + 'receiver_id': receiverID, + 'receiver_fcs_id': receiverFCSID, + 'receiver_name': receiverName, + 'sender_id': senderID, + 'sender_fcs_id': senderFCSID, + 'sender_name': senderName + }; } @override diff --git a/lib/pages/carton/carton_cargo_table.dart b/lib/pages/carton/carton_cargo_table.dart index e21adad..67df836 100644 --- a/lib/pages/carton/carton_cargo_table.dart +++ b/lib/pages/carton/carton_cargo_table.dart @@ -105,9 +105,7 @@ class _CargoTableState extends State { String _t = await showDialog( context: context, builder: (_) => DialogInput( - label: "cargo.qty", - value: c.qty.toString(), - isQty: true)); + label: "cargo.qty", value: c.qty.toString())); if (_t == null) return; setState(() { diff --git a/lib/pages/carton/carton_editor.dart b/lib/pages/carton/carton_editor.dart index ceaf9f3..c56f2ad 100644 --- a/lib/pages/carton/carton_editor.dart +++ b/lib/pages/carton/carton_editor.dart @@ -33,9 +33,7 @@ import 'package:flutter_icons/flutter_icons.dart'; import 'package:provider/provider.dart'; import 'cargo_type_addtion.dart'; import 'carton_cargo_table.dart'; -import 'carton_list_row.dart'; import 'carton_row.dart'; -import 'mix_carton_editor.dart'; import 'model/carton_model.dart'; import '../carton_size/model/carton_size_model.dart'; import 'package_carton_editor.dart'; @@ -62,15 +60,23 @@ class _CartonEditorState extends State { bool _isNew; User _user; String _selectedCartonType; - String _selectedMixBoxType; + double volumetricRatio = 0; double shipmentWeight = 0; FcsShipment _fcsShipment; List _fcsShipments; List _cartons = []; - List _mixCartons = []; CartonSize selectedCatonSize; + //for mix carton + List _mixCartons = []; + String _selectedMixBoxType; + + //for carton from cargos + User consignee; + User sender; + List _cartonsForCargos = []; + @override void initState() { super.initState(); @@ -93,9 +99,20 @@ class _CartonEditorState extends State { _cargoTypes = List.from(_carton.cargoTypes); _isNew = false; _user = User(fcsID: _carton.fcsID, name: _carton.userName); - _loadPackages(); - _getDeliverAddresses(); - _getCartonSize(); + consignee = + User(fcsID: _carton.receiverFCSID, name: _carton.receiverName); + sender = User(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: [], @@ -108,11 +125,6 @@ class _CartonEditorState extends State { _selectedCartonType = carton_from_packages; _selectedMixBoxType = mix_delivery; _loadFcsShipments(); - - // _mixCartons = [ - // Carton(cartonNumber: "A100B-1#1", userName: "Seven 7"), - // Carton(cartonNumber: "A100B-1#2", userName: "Seven 7"), - // ]; } } @@ -188,8 +200,10 @@ class _CartonEditorState extends State { _getDeliverAddresses() async { var addressModel = Provider.of(context, listen: false); - this._deliveryAddresses = - await addressModel.getDeliveryAddresses(_carton.userID); + bool isFromPackages = _carton.cartonType == carton_from_packages; + this._deliveryAddresses = isFromPackages + ? await addressModel.getDeliveryAddresses(_carton.userID) + : await addressModel.getDeliveryAddresses(_carton.receiverID); } _getCartonSize() { @@ -216,6 +230,8 @@ class _CartonEditorState extends State { @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( @@ -348,7 +364,11 @@ class _CartonEditorState extends State { Icons.add_circle, color: primaryColor, ), - onPressed: _addMixCarton, + onPressed: () async { + searchCarton(context, callbackCartonSelect: (c) { + _addMixCarton(c); + }); + }, ), ), ); @@ -405,6 +425,80 @@ class _CartonEditorState extends State { SizedBox(child: heightBox, width: 80), ], ); + + final createMixCarton = LocalButton( + textKey: _isNew ? "box.mix_carton_btn" : "box.cargo.save.btn", + 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, callbackUserSelect: (u) { + setState(() { + this.consignee = u; + }); + })), + ], + ); + + 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, callbackUserSelect: (u) { + setState(() { + this.sender = u; + }); + })), + ], + ); + + 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( @@ -451,29 +545,44 @@ class _CartonEditorState extends State { children: _getMixCartons(context, this._mixCartons)), ] : [ - fcsIDBox, - namebox, - CartonPackageTable( - packages: _carton.packages, - onSelect: (p, checked) { - if (checked && - _deliveryAddress != null && - p.deliveryAddress?.id != _deliveryAddress.id) { - return; - } - setState(() { - p.isChecked = checked; - }); - // _populateDeliveryAddress(); - }, - ), + 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, - this._cartons, - )) + context, + isFromPackages + ? this._cartons + : this._cartonsForCargos)) : Container(), _isNew ? Container() : cargoTableTitleBox, _isNew ? Container() : cargoTableBox, @@ -510,7 +619,12 @@ class _CartonEditorState extends State { SizedBox( height: 20, ), - _isNew ? createBtn : saveBtn, + isFromPackages || isFromCargos + ? _isNew + ? createBtn + : saveBtn + : Container(), + isMixBox ? createMixCarton : Container(), SizedBox( height: 20, ), @@ -525,18 +639,22 @@ class _CartonEditorState extends State { return cartons.asMap().entries.map((c) { return InkWell( onTap: () async { - _loadPackages(); - c.value.packages = _carton.packages; - Carton _c = await Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => - PackageCartonEditor(carton: c.value, isNew: false)), - ); - if (_c == null) return; - cartons.removeWhere((item) => item.id == _c.id); - cartons.insert(c.key, _c); - setState(() {}); + 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)), + ); + if (_c == null) return; + cartons.removeWhere((item) => item.id == _c.id); + cartons.insert(c.key, _c); + setState(() {}); + } }, child: CartonRow( key: ValueKey(c.value.id), @@ -548,12 +666,12 @@ class _CartonEditorState extends State { List _getMixCartons(BuildContext context, List cartons) { return cartons.map((c) { - return InkWell( - onTap: () {}, - child: CartonRow( - key: ValueKey(c.id), - box: c, - ), + return CartonRow( + key: ValueKey(c.id), + box: c, + onRemove: (carton) { + _removeMixCarton(carton); + }, ); }).toList(); } @@ -652,12 +770,25 @@ class _CartonEditorState extends State { _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 (_fcsShipment == null && _isNew) { - showMsgDialog(context, "Error", "Please select FCS shipment"); + + 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; } @@ -669,16 +800,26 @@ class _CartonEditorState extends State { carton.id = _carton.id; carton.cartonType = _selectedCartonType; carton.fcsShipmentID = _isNew ? _fcsShipment.id : _carton.fcsShipmentID; - carton.userID = _user?.id; - carton.fcsID = _user?.fcsID; - carton.userName = _user?.name; - carton.packages = _carton.packages.where((e) => e.isChecked).toList(); + 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 { @@ -691,43 +832,85 @@ class _CartonEditorState extends State { if (_c == null) return; var cartonModel = Provider.of(context, listen: false); Carton _carton = await cartonModel.getCarton(_c.id); - _cartons.add(_carton); + if (isFromPackages) { + _cartons.add(_carton); + } + + if (isFromCargos) { + _cartonsForCargos.add(_carton); + } + setState(() {}); } catch (e) { showMsgDialog(context, "Error", e.toString()); } } - _addMixCarton() async { + _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; - await Navigator.push( - context, - CupertinoPageRoute(builder: (context) => MixCartonEditor(box: carton)), - ); + carton.mixCartons = this._mixCartons; + setState(() { + _isLoading = true; + }); + try { + CartonModel cartonModel = + Provider.of(context, listen: false); + if (_isNew) { + await cartonModel.createMixCarton(carton); + } else { + await cartonModel.updateMixCarton(carton); + } + Navigator.pop(context, true); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } } _save() async { bool isFromPackages = _selectedCartonType == carton_from_packages; - if ((_cargoTypes?.length ?? 0) == 0 && (isFromPackages)) { + 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) { + if ((l <= 0 || w <= 0 || h <= 0) && (isFromPackages || isFromCargos)) { showMsgDialog(context, "Error", "Invalid dimension"); return; } - if (_deliveryAddress == null && (isFromPackages)) { + if (_deliveryAddress == null && (isFromPackages || isFromCargos)) { showMsgDialog(context, "Error", "Invalid delivery address"); return; } @@ -736,8 +919,15 @@ class _CartonEditorState extends State { carton.id = _carton.id; carton.cartonType = _selectedCartonType; carton.fcsShipmentID = _isNew ? _fcsShipment.id : _carton.fcsShipmentID; - carton.userID = _user?.id; - carton.packages = _carton.packages.where((e) => e.isChecked).toList(); + 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; diff --git a/lib/pages/carton/carton_info.dart b/lib/pages/carton/carton_info.dart index d9b4cb4..925f494 100644 --- a/lib/pages/carton/carton_info.dart +++ b/lib/pages/carton/carton_info.dart @@ -51,19 +51,21 @@ class _CartonInfoState extends State { double volumetricRatio = 0; double shipmentWeight = 0; String selectMixBoxType; - List _cartons = []; + List _mixCartons = []; bool isMixBox; bool isFromShipments; bool isFromPackages; bool isSmallBag; + bool isFromCartons; bool isEdiable; @override void initState() { super.initState(); _box = widget.box; - + // _box.cartonType = carton_from_cargos; + // _box.cartonType = carton_mix_box; //for shipment weight volumetricRatio = Provider.of(context, listen: false) .rate @@ -86,21 +88,13 @@ class _CartonInfoState extends State { isFromShipments = _box.cartonType == carton_from_shipments; isFromPackages = _box.cartonType == carton_from_packages; isSmallBag = _box.cartonType == carton_small_bag; - isEdiable = !isMixBox && - (isFromPackages || isSmallBag) && + isFromCartons = _box.cartonType == carton_from_cargos; + + isEdiable = (isFromPackages || isMixBox || isFromCartons) && _box.status == carton_packed_status; - selectMixBoxType = _box.mixBoxType ?? "Mix Delivery"; + selectMixBoxType = _box.mixBoxType ?? ""; + _mixCartons = _box.mixCartons == null ? [] : _box.mixCartons; getCartonSize(); - _cartons = [ - Carton( - cartonNumber: "A100B-1#1", - cargoTypes: [CargoType(name: "General", weight: 12)], - userName: "Seven 7"), - Carton( - cartonNumber: "A100B-1#2", - cargoTypes: [CargoType(name: "General", weight: 12)], - userName: "Seven 7"), - ]; } getCartonSize() { @@ -180,6 +174,55 @@ class _CartonInfoState extends State { iconData: Icons.person, ); + final consigneefcsIDBox = DisplayText( + text: _box.receiverFCSID != null ? _box.receiverFCSID : "", + labelTextKey: "processing.fcs.id", + icon: FcsIDIcon(), + ); + + final consigneeNameBox = DisplayText( + text: _box.receiverName != null ? _box.receiverName : "", + 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: _box.senderFCSID != null ? _box.senderFCSID : "", + labelTextKey: "processing.fcs.id", + icon: FcsIDIcon(), + )), + ], + ); + + final shipperNamebox = DisplayText( + text: _box.senderName != null ? _box.senderName : "", + labelTextKey: "processing.shipper.name", + maxLines: 2, + iconData: Icons.person, + ); + + final shipperBox = Container( + child: Column( + children: [ + shipperIDBox, + shipperNamebox, + ], + ), + ); + final lengthBox = LengthPicker( controller: _lengthController, lableKey: "box.length", @@ -291,17 +334,29 @@ class _CartonInfoState extends State { cartonTypeBox, LocalTitle(textKey: "box.shipment_info"), shipmentBox, - isSmallBag ? mixCartonNumberBox : Container(), - isMixBox ? Container() : fcsIDBox, - isMixBox ? Container() : customerNameBox, - isMixBox ? mixTypeBox : Container(), - isMixBox ? LocalTitle(textKey: "boxes.title") : Container(), + // isSmallBag ? mixCartonNumberBox : Container(), isMixBox - ? Column( - children: _getCartons( - context, - this._cartons, - )) + ? Container() + : isFromPackages + ? fcsIDBox + : Container(), + isMixBox + ? Container() + : isFromPackages + ? customerNameBox + : Container(), + isFromCartons + ? Row( + children: [ + Flexible(child: consigneeBox), + Flexible(child: shipperBox) + ], + ) + : Container(), + isMixBox ? mixTypeBox : Container(), + isMixBox ? LocalTitle(textKey: "box.mix_caton_title") : Container(), + isMixBox + ? Column(children: _getCartons(context, this._mixCartons)) : Container(), isFromPackages || isSmallBag ? CartonPackageTable( @@ -310,7 +365,7 @@ class _CartonInfoState extends State { : Container(), isMixBox ? Container() : LocalTitle(textKey: "box.cargo.type"), isMixBox ? Container() : cargoTableBox, - ...(isFromPackages + ...(isFromPackages || isFromCartons ? [ LocalTitle(textKey: "box.dimension"), cartonSizeBox, diff --git a/lib/pages/carton/mix_carton_editor.dart b/lib/pages/carton/mix_carton_editor.dart deleted file mode 100644 index f86c530..0000000 --- a/lib/pages/carton/mix_carton_editor.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'package:fcs/domain/entities/carton.dart'; -import 'package:fcs/helpers/theme.dart'; -import 'package:fcs/pages/carton/model/carton_model.dart'; -import 'package:fcs/pages/carton_search/carton_search.dart'; -import 'package:fcs/pages/main/util.dart'; -import 'package:fcs/pages/widgets/local_button.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:provider/provider.dart'; -import 'carton_row.dart'; - -class MixCartonEditor extends StatefulWidget { - final Carton box; - MixCartonEditor({this.box}); - - @override - _MixCartonEditorState createState() => _MixCartonEditorState(); -} - -class _MixCartonEditorState extends State { - Carton _box; - bool _isLoading = false; - bool _isNew; - List _cartons = []; - - @override - void initState() { - super.initState(); - _box = widget.box; - } - - @override - Widget build(BuildContext context) { - final createBtn = LocalButton( - textKey: "box.mix_carton_btn", - callBack: _creatCarton, - ); - - final cargoTableTitleBox = LocalTitle( - textKey: "boxes.title", - trailing: IconButton( - icon: Icon( - Icons.add_circle, - color: primaryColor, - ), - onPressed: () => searchCarton(context, callbackCartonSelect: (c) { - _addMixCarton(c); - }), - ), - ); - - return LocalProgress( - inAsyncCall: _isLoading, - child: Scaffold( - appBar: AppBar( - centerTitle: true, - leading: new IconButton( - icon: new Icon( - CupertinoIcons.back, - color: primaryColor, - ), - onPressed: () => Navigator.of(context).pop(), - ), - shadowColor: Colors.transparent, - backgroundColor: Colors.white, - title: LocalText( - context, - "box.min_caton.form.title", - fontSize: 20, - color: primaryColor, - ), - ), - body: Padding( - padding: const EdgeInsets.all(8.0), - child: ListView( - children: [ - cargoTableTitleBox, - Column( - children: _getCartons( - context, - this._cartons, - )), - SizedBox( - height: 20, - ), - createBtn - ], - ), - ), - ), - ); - } - - List _getCartons(BuildContext context, List cartons) { - return cartons.map((c) { - return CartonRow( - key: ValueKey(c.id), - box: c, - onRemove: (carton) { - _removeMixCarton(carton); - }, - ); - }).toList(); - } - - _addMixCarton(Carton carton) { - if (carton == null) return; - if (this._cartons.any((c) => c.cartonNumber == carton.cartonNumber)) return; - setState(() { - this._cartons.add(carton); - }); - } - - _removeMixCarton(Carton carton) { - setState(() { - this._cartons.remove(carton); - }); - } - - _creatCarton() { - if ((this._cartons?.length ?? 0) == 0) { - showMsgDialog(context, "Error", "Expect at least one carton"); - return; - } - Carton carton = Carton(); - carton.id = _box.id; - carton.cartonType = _box.cartonType; - setState(() { - _isLoading = true; - }); - try { - CartonModel cartonModel = - Provider.of(context, listen: false); - } catch (e) { - showMsgDialog(context, "Error", e.toString()); - } finally { - setState(() { - _isLoading = false; - }); - } - } -} diff --git a/lib/pages/carton/model/carton_model.dart b/lib/pages/carton/model/carton_model.dart index d0c20e4..c664df8 100644 --- a/lib/pages/carton/model/carton_model.dart +++ b/lib/pages/carton/model/carton_model.dart @@ -13,6 +13,7 @@ import 'package:logging/logging.dart'; class CartonModel extends BaseModel { List _boxes = []; + List cartons = []; final log = Logger('CartonModel'); List get boxes => _selectedIndex == 1 ? _boxes : List.from(_delivered.values); @@ -22,6 +23,7 @@ class CartonModel extends BaseModel { bool isLoading = false; StreamSubscription listener; + StreamSubscription cartonListener; static List statusHistory = [ ShipmentStatus(status: "Packed", date: DateTime(2020, 6, 1), done: true), ShipmentStatus(status: "Shipped", date: DateTime(2020, 6, 5), done: false), @@ -56,10 +58,15 @@ class CartonModel extends BaseModel { }); } - List cartonTypes = [carton_from_packages, carton_mix_box]; + List cartonTypes = [ + carton_from_packages, + carton_from_cargos, + carton_mix_box + ]; List mixBoxTypes = [mix_delivery, mix_pickup]; List cartonTypesInfo = [ carton_from_packages, + carton_from_cargos, carton_mix_box, carton_from_shipments, carton_small_bag @@ -75,6 +82,7 @@ class CartonModel extends BaseModel { initData() { _selectedIndex = 1; _loadBoxes(); + _loadCartonForMixBox(); if (_delivered != null) _delivered.close(); _delivered = _getDelivered(); @@ -108,6 +116,35 @@ class CartonModel extends BaseModel { } } + Future _loadCartonForMixBox() async { + if (user == null || !user.hasCarton()) return; + String path = "/$cartons_collection/"; + if (cartonListener != null) cartonListener.cancel(); + cartons = []; + try { + cartonListener = Firestore.instance + .collection("$path") + .where("carton_type", + whereIn: [carton_from_packages, carton_from_cargos]) + .where("status", isEqualTo: carton_packed_status) + .where("is_deleted", isEqualTo: false) + .orderBy("user_name") + .snapshots() + .listen((QuerySnapshot snapshot) { + cartons.clear(); + cartons = snapshot.documents.map((documentSnapshot) { + var s = Carton.fromMap( + documentSnapshot.data, documentSnapshot.documentID); + return s; + }).toList(); + + notifyListeners(); + }); + } catch (e) { + log.warning("Error!! $e"); + } + } + Paginator _getDelivered() { if (user == null || !user.hasCarton()) return null; @@ -145,8 +182,10 @@ class CartonModel extends BaseModel { @override logout() async { if (listener != null) await listener.cancel(); + if (cartonListener != null) await cartonListener.cancel(); if (_delivered != null) _delivered.close(); _boxes = []; + cartons = []; } Future> getCartons(String shipmentID) async { @@ -220,38 +259,14 @@ class CartonModel extends BaseModel { } Future> searchCarton(String term) async { - if (term == null || term == '') return List(); + return Services.instance.cartonService.searchCarton(term); + } - List _cartons = []; + Future createMixCarton(Carton carton) { + // return Services.instance.cartonService.createCarton(carton); + } - try { - _cartons = [ - Carton( - cartonNumber: "A100B-1#1", - cargoTypes: [CargoType(name: "General", weight: 12)], - userName: "Seven 7"), - Carton( - cartonNumber: "A100B-1#2", - cargoTypes: [CargoType(name: "General", weight: 12)], - userName: "Seven 7"), - Carton( - cartonNumber: "A100B-1#3", - cargoTypes: [CargoType(name: "General", weight: 12)], - userName: "Seven 7"), - Carton( - cartonNumber: "A100B-1#4", - cargoTypes: [CargoType(name: "General", weight: 12)], - userName: "Seven 7"), - Carton( - cartonNumber: "A100B-1#5", - cargoTypes: [CargoType(name: "General", weight: 12)], - userName: "Seven 7"), - ]; - } catch (e) { - // permission error - log.warning("user error:" + e.toString()); - return null; - } - return _cartons; + Future updateMixCarton(Carton carton) { + // return Services.instance.cartonService.updateCarton(carton); } } diff --git a/lib/pages/carton/package_carton_editor.dart b/lib/pages/carton/package_carton_editor.dart index b563a28..9fde587 100644 --- a/lib/pages/carton/package_carton_editor.dart +++ b/lib/pages/carton/package_carton_editor.dart @@ -1,3 +1,4 @@ +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'; @@ -46,6 +47,8 @@ class _PackageCartonEditorState extends State { List _deliveryAddresses = []; List _cargoTypes = []; CartonSize selectedCatonSize; + bool isFromPackages; + bool isFromCargos; @override void initState() { @@ -55,6 +58,8 @@ class _PackageCartonEditorState extends State { _load() { _carton = widget.carton; + isFromPackages = _carton.cartonType == carton_from_packages; + isFromCargos = _carton.cartonType == carton_from_cargos; _getDeliverAddresses(); if (widget.isNew) { _lengthCtl.text = "0"; @@ -73,8 +78,10 @@ class _PackageCartonEditorState extends State { _getDeliverAddresses() async { var addressModel = Provider.of(context, listen: false); - this._deliveryAddresses = - await addressModel.getDeliveryAddresses(_carton.userID); + + this._deliveryAddresses = isFromPackages + ? await addressModel.getDeliveryAddresses(_carton.userID) + : await addressModel.getDeliveryAddresses(_carton.receiverID); } _getCartonSize() { @@ -321,9 +328,21 @@ class _PackageCartonEditorState extends State { carton.id = _carton.id; carton.cartonType = _carton.cartonType; carton.fcsShipmentID = _carton.fcsShipmentID; - carton.userID = _carton.userID; carton.cargoTypes = _cargoTypes; - carton.packages = _carton.packages.where((e) => e.isChecked).toList(); + if (isFromPackages) { + carton.userID = _carton.userID; + carton.packages = _carton.packages.where((e) => e.isChecked).toList(); + } + + if (isFromCargos) { + carton.receiverID = _carton.receiverID; + carton.receiverFCSID = _carton.receiverFCSID; + carton.receiverName = _carton.receiverName; + carton.senderID = _carton.senderID; + carton.senderFCSID = _carton.senderFCSID; + carton.senderName = _carton.senderName; + } + carton.length = l; carton.width = w; carton.height = h; diff --git a/lib/pages/carton_search/carton_search.dart b/lib/pages/carton_search/carton_search.dart index 741fc83..9b131d2 100644 --- a/lib/pages/carton_search/carton_search.dart +++ b/lib/pages/carton_search/carton_search.dart @@ -1,8 +1,11 @@ import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/helpers/theme.dart'; import 'package:fcs/pages/carton/model/carton_model.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:fcs/pages/widgets/barcode_scanner.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons/flutter_icons.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'carton_list_row.dart'; @@ -21,7 +24,7 @@ class PartSearchDelegate extends SearchDelegate { PartSearchDelegate({this.callbackCartonSelect}); @override - String get searchFieldLabel => 'Search by Carton Number/Customer Name'; + String get searchFieldLabel => 'Search by Carton Number'; @override ThemeData appBarTheme(BuildContext context) { @@ -40,6 +43,11 @@ class PartSearchDelegate extends SearchDelegate { @override List buildActions(BuildContext context) { return [ + IconButton( + icon: Icon(MaterialCommunityIcons.barcode_scan, + size: 30, color: Colors.white), + onPressed: () => _scan(context), + ), IconButton( icon: Icon(Icons.clear), onPressed: () => query = '', @@ -106,17 +114,41 @@ class PartSearchDelegate extends SearchDelegate { @override Widget buildSuggestions(BuildContext context) { + final cartonModel = Provider.of(context); return Container( - child: Center( - child: Opacity( - opacity: 0.2, - child: Icon( - MaterialCommunityIcons.package, - color: primaryColor, - size: 200, - ), - ), + padding: EdgeInsets.only(top: 5), + child: ListView( + children: cartonModel.cartons + .map((u) => CartonListRow( + carton: u, + callbackCartonSelect: callbackCartonSelect, + )) + .toList(), ), ); } + + _scan(BuildContext context) async { + PermissionStatus permission = + await PermissionHandler().checkPermissionStatus(PermissionGroup.camera); + if (permission != PermissionStatus.granted) { + Map permissions = + await PermissionHandler() + .requestPermissions([PermissionGroup.camera]); + if (permissions[PermissionGroup.camera] != PermissionStatus.granted) { + showMsgDialog(context, "Error", "Camera permission is not granted"); + return null; + } + } + + try { + String barcode = await scanBarcode(); + if (barcode != null) { + query = barcode; + showResults(context); + } + } catch (e) { + print('error: $e'); + } + } } diff --git a/lib/pages/customer/invitation_create.dart b/lib/pages/customer/invitation_create.dart index 51fd10c..af2e2ed 100644 --- a/lib/pages/customer/invitation_create.dart +++ b/lib/pages/customer/invitation_create.dart @@ -172,7 +172,7 @@ class _InvitationCreateState extends State { } isDataChanged() { - String userName = _nameController.text; + String userName = _nameController.text; String phoneNumber = _phoneController.text; return userName != "" || phoneNumber != ""; } diff --git a/lib/pages/widgets/dialog_input.dart b/lib/pages/widgets/dialog_input.dart index 3c63f84..6ebe4b0 100644 --- a/lib/pages/widgets/dialog_input.dart +++ b/lib/pages/widgets/dialog_input.dart @@ -7,9 +7,8 @@ import 'local_text.dart'; class DialogInput extends StatefulWidget { final String value; final String label; - final bool isQty; - const DialogInput({Key key, this.label, this.value, this.isQty = false}) - : super(key: key); + + const DialogInput({Key key, this.label, this.value}) : super(key: key); @override _DialogInputState createState() => _DialogInputState(); } @@ -50,7 +49,7 @@ class _DialogInputState extends State { key: _formKey, child: TextField( controller: _controller, - focusNode: widget.isQty ? _focusNode : FocusNode(), + focusNode: _focusNode, autofocus: true, keyboardType: TextInputType.number, decoration: new InputDecoration(