add structure

This commit is contained in:
2020-05-29 07:45:27 +06:30
parent 4c851d9971
commit bad27ba5c4
272 changed files with 36065 additions and 174 deletions

View File

@@ -0,0 +1,61 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/vo/announcement.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class AnnouncementModel extends BaseModel {
List<Announcement> announcements = [];
void initUser(user) {
super.initUser(user);
_loadAnnouncements();
}
@override
logout() async {
announcements = [];
}
Future<void> _loadAnnouncements() async {
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(
"/$biz_collection/${setting.okEnergyId}/$announcement_collection")
.snapshots();
snapshots.listen((snaps) async {
announcements = snaps.documents.map((documentSnapshot) {
var data = Announcement.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return data;
}).toList();
notifyListeners();
});
}
Future<Announcement> getAnnouncement(String id) async {
String path = "/$biz_collection/${setting.okEnergyId}/$announcement_collection";
var snap = await getDocSnap(path, id);
return Announcement.fromMap(snap.data, snap.documentID);
}
Future<void> createAnnouncement(Announcement announcement) async {
await request("/announcement", "POST",
payload: announcement.toMap(), token: await getToken());
}
Future<void> updateAnnouncement(Announcement announcement) async {
await request("/announcement", "PUT",
payload: announcement.toMap(), token: await getToken());
}
Future<void> deleteAnnouncement(Announcement announcement) async {
await request("/announcement", "DELETE",
payload: announcement.toMap(), token: await getToken());
}
}

147
lib/model/api_helper.dart Normal file
View File

@@ -0,0 +1,147 @@
import 'dart:convert';
import 'dart:io';
import 'package:device_info/device_info.dart';
import 'package:dio/dio.dart';
import 'package:logging/logging.dart';
import 'package:fcs/vo/status.dart';
import '../config.dart';
final log = Logger('requestAPI');
// request makes http request
// if token is null
Future<dynamic> requestAPI(
String path,
method, {
dynamic payload,
String token,
String url,
}) async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
String deviceName = "${androidInfo.model}(${androidInfo.id})";
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
Map<String, dynamic> headers = {};
if (token != null) {
headers["Token"] = token;
}
if (androidInfo.androidId != null) {
headers["Device"] = androidInfo.androidId + ":" + deviceName;
}
headers["Project-ID"] = Config.instance.reportProjectID;
BaseOptions options = new BaseOptions(
method: method,
baseUrl: url == null ? Config.instance.apiURL : url,
connectTimeout: 10000,
receiveTimeout: 10000,
headers: headers,
);
log.info("baseUrl:${options.baseUrl}, path:$path");
try {
Dio dio = new Dio(options);
Response response = await dio.request(
path,
data: payload,
);
var data = Status.fromJson(response.data);
if (data.status == 'Ok') {
return response.data["data"];
} else {
throw Exception(data.message);
}
} catch (e) {
log.warning("path:$path, api:$e");
throw e;
}
}
// request makes http request
// if token is null
Future<dynamic> requestDownloadAPI(String path, method,
{dynamic payload, String token, String url, String filePath}) async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
String deviceName = "${androidInfo.model}(${androidInfo.id})";
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
var bytes = utf8.encode(payload);
var base64Str = base64.encode(bytes);
String escapePayload = HtmlEscape().convert(base64Str);
try {
String baseUrl = url == null ? Config.instance.apiURL : url;
log.info("Path:$baseUrl$path");
HttpClient client = new HttpClient();
var _downloadData = StringBuffer();
var fileSave = new File(filePath);
var request = await client.getUrl(Uri.parse("$baseUrl$path"));
request.headers.set("Project-ID", Config.instance.reportProjectID);
request.headers
.set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8");
if (token != null) {
request.headers.set("Token", token);
}
if (androidInfo.androidId != null) {
request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
}
request.headers.set("payload", escapePayload);
var response = await request.close();
print("headers:${response.headers}");
response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
onDone: () {
fileSave.writeAsString(_downloadData.toString());
});
} catch (e) {
log.warning("path:$path, api:$e");
throw e;
}
}
// request makes http request
// if token is null
Future<dynamic> requestDownloadPDFAPI(String path, method,
{dynamic payload, String token, String url, String filePath}) async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
String deviceName = "${androidInfo.model}(${androidInfo.id})";
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
var bytes = utf8.encode(payload);
var base64Str = base64.encode(bytes);
String escapePayload = HtmlEscape().convert(base64Str);
try {
String baseUrl = url == null ? Config.instance.apiURL : url;
log.info("Path:$baseUrl$path");
HttpClient client = new HttpClient();
// var _downloadData = StringBuffer();
var fileSave = new File(filePath);
var request = await client.getUrl(Uri.parse("$baseUrl$path"));
request.headers.set("Project-ID", Config.instance.reportProjectID);
if (token != null) {
request.headers.set("Token", token);
}
if (androidInfo.androidId != null) {
request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
}
request.headers.set("payload", escapePayload);
var response = await request.close();
print("headers:${response.headers}");
var _downloadData = List<int>();
response.listen((d) => _downloadData.addAll(d), onDone: () {
fileSave.writeAsBytes(_downloadData);
});
// response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
// onDone: () {
// fileSave.writeAsString(_downloadData.toString());
// });
} catch (e) {
log.warning("path:$path, api:$e");
throw e;
}
}

36
lib/model/base_model.dart Normal file
View File

@@ -0,0 +1,36 @@
import 'package:flutter/foundation.dart';
import 'package:fcs/model/api_helper.dart';
import 'package:fcs/model/main_model.dart';
import '../vo/setting.dart';
import '../vo/user.dart';
abstract class BaseModel extends ChangeNotifier {
User user;
Setting setting;
MainModel mainModel;
void initUser(User user) async {
this.user = user;
}
void initSetting(Setting setting) async {
this.setting = setting;
}
void logout();
// request makes http request
// if token is null
dynamic request(
String path,
method, {
dynamic payload,
String token,
String url,
}) async {
mainModel.resetPinTimer();
return await requestAPI(path, method,
payload: payload, token: token, url: url);
}
}

177
lib/model/buyer_model.dart Normal file
View File

@@ -0,0 +1,177 @@
import 'dart:async';
import 'dart:convert' show HtmlEscape, base64, utf8;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:fcs/config.dart';
import 'package:fcs/theme/theme.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class BuyerModel extends BaseModel {
final log = Logger('BuyerModel');
StreamSubscription<QuerySnapshot> listener;
List<Buyer> buyers = [];
PopupMenu popupMenu = new PopupMenu(index: 0);
PopupMenu sortMenu = new PopupMenu();
Buyer searchBuyer;
void initUser(user) {
super.initUser(user);
_loadBuyers();
}
Future<void> _loadBuyers() async {
if (!user.isOwnerAndAbove() && !user.hasBuyer()) {
return;
}
listener = getQuerySnapshotByOrder(
"/$biz_collection/${setting.okEnergyId}/$buyer_collection",
'user_name')
.listen((snaps) async {
buyers.clear();
snaps.documents.forEach((d) {
buyers.add(Buyer.fromMap(d.data, d.documentID));
});
notifyListeners();
});
}
@override
logout() async {
if (listener != null) await listener.cancel();
buyers = [];
}
Future<Buyer> getBuyer(String buyerID) async {
var snap = await getDocSnap(
"/$biz_collection/${setting.okEnergyId}/$buyer_collection", buyerID);
return Buyer.fromMap(snap.data, snap.documentID);
}
Future<Buyer> loadBuyerProducts(Buyer buyer, {bool force = false}) async {
if (!force && buyer.buyerProducts != null && buyer.buyerProducts.length > 0)
return buyer;
var snaps = await getSnapshot(
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${buyer.id}/$product_collection");
buyer.buyerProducts = snaps.documents
.map((s) => BuyerProduct.fromMap(s.data, s.documentID))
.toList();
return buyer;
}
Future<void> delete(Buyer buyer) async {
await request("/buyer/${buyer.id}", "DELETE", token: await getToken());
}
Future<void> approve(Buyer buyer) async {
await request("/buyer/approve", "PUT",
payload: buyer.toMap(), token: await getToken());
}
Future<void> reject(Buyer buyer) async {
await request("/buyer/reject", "POST",
payload: buyer.toMap(), token: await getToken());
}
Future<void> allocate(Buyer buyer) async {
await request("/buyer/allocate", "POST",
payload: buyer.toMap(), token: await getToken());
}
void filterStatus(String status, int _selectedIndex, int _sleectedSortIndex) {
this.sortMenu.index = _sleectedSortIndex;
buyers.clear();
if (listener != null) {
listener.cancel();
}
this.popupMenu.index = _selectedIndex;
String path = "/$biz_collection/${setting.okEnergyId}/$buyer_collection";
listener = getFilterStatusSnapshot(path, status, 'user_name')
.listen((snaps) async {
buyers.clear();
snaps.documents.forEach((d) {
buyers.add(Buyer.fromMap(d.data, d.documentID));
});
notifyListeners();
});
notifyListeners();
}
Future<List<Buyer>> search(String searchBuyer) async {
if (searchBuyer == null || searchBuyer == '') return List();
var bytes = utf8.encode(searchBuyer);
var base64Str = base64.encode(bytes);
HtmlEscape htmlEscape = const HtmlEscape();
String escapeBuyer = htmlEscape.convert(base64Str);
int limit = 20;
List<Buyer> _buyers = [];
try {
var data = await request(
"/api/fts/$buyer_collection/$escapeBuyer/$limit", "GET",
token: await getToken(), url: Config.instance.reportURL);
if (data == null) return List();
data.forEach((buyer) {
var _buyer = Buyer.fromJson(buyer);
_buyers.add(_buyer);
});
} catch (e) {
// permission error
log.warning("buyer error:" + e.toString());
return null;
}
return _buyers;
}
void filterSorting(int _sleectedSortIndex, int _selectedIndex) {
this.popupMenu.index = _selectedIndex;
buyers.clear();
if (listener != null) {
listener.cancel();
}
String _fieldName;
bool descending = false;
if (_sleectedSortIndex == 0) {
_fieldName = 'user_name';
descending = false;
}
if (_sleectedSortIndex == 1) {
_fieldName = 'user_name';
descending = true;
}
if (_sleectedSortIndex == 2) {
_fieldName = 'phone_number';
descending = false;
}
if (_sleectedSortIndex == 3) {
_fieldName = 'phone_number';
descending = true;
}
this.sortMenu.index = _sleectedSortIndex;
String path = "/$biz_collection/${setting.okEnergyId}/$buyer_collection";
listener =
getFilterSnapshot(path, descending, _fieldName).listen((snaps) async {
buyers.clear();
snaps.documents.forEach((d) {
buyers.add(Buyer.fromMap(d.data, d.documentID));
notifyListeners();
});
notifyListeners();
});
notifyListeners();
}
}

244
lib/model/chart_model.dart Normal file
View File

@@ -0,0 +1,244 @@
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:fcs/config.dart';
import 'package:fcs/model/firebase_helper.dart';
import 'package:fcs/vo/po.dart';
import 'package:fcs/vo/po_do_count.dart';
import 'package:fcs/vo/revenue.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class ChartModel extends BaseModel {
final log = Logger('ChartModel');
List<POChartData> chartSummary = [];
Revenue revenue = new Revenue();
PODOCount podoCount = new PODOCount();
void initUser(user) async {
super.initUser(user);
revenue = new Revenue();
podoCount = new PODOCount();
_loadRev();
_loadPODOCount();
if (user.hasPO() || user.isOwnerAndAbove()) {
_getSummary();
}
}
Future<void> _loadRev() async {
try {
String path = "/$biz_collection/${setting.okEnergyId}/$report_collection";
String reportID = "revenue";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$report_collection";
reportID = "spending";
}
getDocSnapshot(path, reportID).listen((DocumentSnapshot snapshot) async {
if (snapshot.data == null) {
return;
}
revenue = Revenue.fromMap(snapshot.data, snapshot.documentID);
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<void> _loadPODOCount() async {
try {
String path = "/$biz_collection/${setting.okEnergyId}/$report_collection";
String reportID = "po_do_count";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$report_collection";
}
getDocSnapshot(path, reportID).listen((DocumentSnapshot snapshot) async {
if (snapshot.data == null) {
return;
}
podoCount = PODOCount.fromMap(snapshot.data, snapshot.documentID);
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
@override
logout() async {
chartSummary = [];
revenue = new Revenue();
podoCount = new PODOCount();
}
_getSummary() async {
chartSummary.clear();
try {
var data = {
"fields": 'quantity,product_id,product_name,color',
"aggfuns": "sum,,,",
"groupbys": 'product_id',
};
var result = await request("/api/data/po_product_view", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
if (result == null) return;
chartSummary.clear();
result.forEach((chart) {
var _list = POChartData.fromJson(chart, "quantity_sum");
chartSummary.add(_list);
});
} catch (e) {
log.warning("Error get Summary>>>>${e.toString()}");
}
}
Future<List<POChartData>> loadSummary() async {
List<POChartData> _list = List();
try {
var data = {
"fields": 'quantity,product_id,product_name,color',
"aggfuns": "sum,,,",
"groupbys": 'product_id',
};
var result = await request("/api/data/po_product_view", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
if (result != null) {
result.forEach((chart) {
var _data = POChartData.fromJson(chart, "quantity_sum");
_list.add(_data);
});
}
return _list;
} catch (e) {
log.warning("Error>>>>${e.toString()}");
return null;
}
}
Future<List<POChartData>> loadUsers() async {
List<POChartData> _list = List();
var data = {
"fields": 'quantity,user_name,product_name',
"aggfuns": "sum,,",
"groupbys": 'product_id,user_id',
};
try {
var result = await request("/api/data/po_product_view", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
result.forEach((chart) {
var _buyer = POChartData.fromJson(chart, "quantity_sum");
_list.add(_buyer);
});
return _list;
} catch (e) {
log.warning("Error load>>>>${e.toString()}");
return null;
}
}
Future<List<POBuyerData>> loadPOBalancesForBuyer_() async {
List<POBuyerData> _list = List();
try {
var data = {
"fields": 'amount,status',
"aggfuns": "sum,",
"groupbys": 'status',
"filters": [
{"field": "user_id", "compare": "==", "value": user.docID}
]
};
var result = await request("/api/data/po_buyer_view", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
if (result != null) {
result.forEach((chart) {
var _data = POBuyerData.fromJson(chart, "amount_sum");
_list.add(_data);
});
}
return _list;
} catch (e) {
log.warning("Error>>>>${e.toString()}");
return null;
}
}
Future<List<POChartData>> loadPOBalancesForBuyer() async {
List<POChartData> _list = List();
try {
var data = {
"fields": 'quantity,product_id,product_name,user_id,color',
"aggfuns": "sum,,,,",
"groupbys": 'product_id,user_id',
"filters": [
{"field": "user_id", "compare": "==", "value": user.docID}
]
};
var result = await request("/api/data/po_product_view", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
if (result != null) {
result.forEach((chart) {
var _data = POChartData.fromJson(chart, "quantity_sum");
_list.add(_data);
});
}
return _list;
} catch (e) {
log.warning("Error>>>>${e.toString()}");
return null;
}
}
Future<List<POChartData>> loadPOBalProductsForBuyer() async {
List<POChartData> _list = List();
var data = {
"fields": 'quantity,user_name,user_id,product_name',
"aggfuns": "sum,,,",
"groupbys": 'product_id,user_id',
"filters": [
{"field": "user_id", "compare": "==", "value": user.docID}
]
};
try {
var result = await request("/api/data/po_product_view", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
result.forEach((chart) {
var _buyer = POChartData.fromJson(chart, "quantity_sum");
_list.add(_buyer);
});
return _list;
} catch (e) {
log.warning("Error load>>>>${e.toString()}");
return null;
}
}
}

37
lib/model/constants.dart Normal file
View File

@@ -0,0 +1,37 @@
const ok_doc_id = "ok";
const setting_doc_id = "ok_setting";
const config_collection = "configs";
const biz_collection = "bizs";
const product_collection = "products";
const user_collection = "users";
const privilege_collection = "privileges";
const user_level_collection = "user_levels";
const storage_collection = "storages";
const buyer_collection = "buyers";
const buying_pos = "buying_pos";
const selling_pos = "selling_pos";
const inventory_takings = "inventory_takings";
const inventory_lines = "inventory_lines";
const pds_collection = "pds";
const pos_collection = "pos";
const dos_collection = "dos";
const notification_collection = "notifications";
const log_collection = "logs";
const report_collection = "reports";
const po_product_collection = "po_products";
const device_collection = "devices";
const do_po_lines_collection = "do_po_lines";
const reports_collection = "reports";
const announcement_collection = "announcements";
const report_user_collection = "report_users";
const po_files_path = "/ok/po";
const reg_files_path = "/ok/reg";
const do_files_path = "/ok/do";
const sign_files_path = "/ok/sign";
const bank_images_path = "/ok/banks";
const po_approved_status = "approved";
const po_closed_status = "closed";
const do_approved_status = "approved";

View File

@@ -0,0 +1,140 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:path/path.dart' as Path;
import 'package:fcs/model/constants.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'base_model.dart';
import 'firebase_helper.dart';
class DeliveryModel extends BaseModel {
StreamSubscription<QuerySnapshot> listener;
List<DOSubmission> dos = [];
PopupMenu popupMenu = new PopupMenu(index: 0);
int dateIndex = 0;
DateTime selectedDate = DateTime.now();
int timber = 0;
void initUser(user) async {
super.initUser(user);
_loadDOs();
}
Future<void> _loadDOs() async {
if (!user.isOwnerAndAbove() && !user.hasDelivery()) {
return;
}
String path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
var startDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0);
var endDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 23, 59, 59);
listener = getDeliverySnapshot(
path, 'delivery_date', startDate, endDate, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
}
@override
logout() async {
if (listener != null) await listener.cancel();
dos = [];
}
Future<DOSubmission> getDO(String id) async {
String path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection";
}
var doSnap = await getDocSnap(path, id);
return DOSubmission.fromMap(doSnap.data, doSnap.documentID);
}
Future<void> endDelivery(DOSubmission doObj, Uint8List img) async {
String path = Path.join(do_files_path, user.docID);
String imgUrl = await uploadStorageData(path, img, fileName: doObj.id);
doObj.doReceiptUrl = imgUrl;
await request("/do/ended", "POST",
payload: doObj.toMap(), token: await getToken());
}
void filterData(
String status, DateTime dateTime, int _selectedIndex, int _dateIndex) {
dos.clear();
var startDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 0, 0, 0);
var endDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59);
if (listener != null) {
listener.cancel();
}
this.popupMenu.index = _selectedIndex;
this.dateIndex = _dateIndex;
this.selectedDate = dateTime == null
? new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0)
: dateTime;
String path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection";
}
if (status != null && dateTime == null) {
listener = getDeliveryStatusSnapshot(path, status, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else if (dateTime != null && status == null) {
listener = getDeliveryDateSnapshot(
path, 'delivery_date', startDate, endDate, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else if (status != null && dateTime != null) {
listener = getDeliveryDataSnapshot(
path, status, 'delivery_date', startDate, endDate, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else {
listener =
getQuerySnapshotByOrder(path, 'do_number').listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
}
}
}

View File

@@ -0,0 +1,76 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:device_info/device_info.dart';
import 'package:fcs/vo/device.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class PhoneDeviceModel extends BaseModel {
List<PhoneDevice> devices = new List();
bool isLogout = false;
void initUser(user) {
super.initUser(user);
_loadDevices();
}
Future<void> _loadDevices() async {
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(
"/$biz_collection/${setting.okEnergyId}/$user_collection/${user.docID}/$device_collection")
.where("primary_device", isEqualTo: false)
.snapshots();
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
snapshots.listen((snaps) async {
devices = snaps.documents.map((documentSnapshot) {
var data = PhoneDevice.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
if (!data.deviceOn &&
!data.primaryDevice &&
data.id == androidInfo.androidId &&
!documentSnapshot.metadata.isFromCache) {
this.isLogout = true;
this.mainModel.logout();
notifyListeners();
}
return data;
}).toList();
notifyListeners();
});
}
@override
logout() async {
devices = [];
}
bool isLogoutDevice() {
return this.isLogout;
}
setDevice(bool status) {
this.isLogout = status;
notifyListeners();
}
Future<void> confirmDevice(String id, String deviceID) async {
await request("/dev/on", "POST",
payload: {"id": id, "device_id": deviceID}, token: await getToken());
}
Future<void> logoutDevice(String id, String deviceID) async {
await request("/dev/off", "POST",
payload: {"id": id, "device_id": deviceID}, token: await getToken());
}
Future<void> setPrimaryDevice(String id, String deviceID) async {
await request("/dev", "PUT",
payload: {"id": id, "device_id": deviceID}, token: await getToken());
}
}

258
lib/model/do_model.dart Normal file
View File

@@ -0,0 +1,258 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:path/path.dart' as Path;
import 'package:fcs/model/api_helper.dart';
import 'package:fcs/model/constants.dart';
import 'package:fcs/pages/do/do_files.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'base_model.dart';
import 'firebase_helper.dart';
class DOModel extends BaseModel {
StreamSubscription<QuerySnapshot> listener;
List<DOSubmission> dos = [];
PopupMenu popupMenu = new PopupMenu(index: 0);
int dateIndex = 0;
DateTime selectedDate = DateTime.now();
int timber = 0;
void initUser(user) async {
super.initUser(user);
_loadDOs();
}
@override
logout() async {
if (listener != null) await listener.cancel();
dos = [];
}
Future<void> _loadDOs() async {
String path;
if (user.hasDO() || user.isOwnerAndAbove()) {
path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
}
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection";
}
var startDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0);
var endDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 23, 59, 59);
listener =
getFilterDateSnapshot(path, 'do_date', startDate, endDate, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
}
Future<DOSubmission> loadDOPOLines(DOSubmission doSub) async {
String path =
"/$biz_collection/${setting.okEnergyId}/$dos_collection/${doSub.id}/$do_po_lines_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection/${doSub.id}/$do_po_lines_collection";
}
var snaps = await getSnapshot(path);
doSub.dopoLies =
snaps.documents.map((s) => DOPOLine.fromMap(s.data)).toList();
return doSub;
}
Future<DOSubmission> getDO(String id) async {
String path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection";
}
var doSnap = await getDocSnap(path, id);
return DOSubmission.fromMap(doSnap.data, doSnap.documentID);
}
Future<void> createDO(DOSubmission doSubmission, DOFiles files) async {
doSubmission.userID = user.docID;
String path = Path.join(do_files_path, user.docID);
if (files.storageChargeFile != null) {
String url = await uploadStorage(path, files.storageChargeFile);
doSubmission.storageReceiptUrl = url;
}
if (files.licenseFile != null) {
String url = await uploadStorage(path, files.licenseFile);
doSubmission.driverLicenceUrl = url;
}
await request("/do", "POST",
payload: doSubmission.toMap(), token: await getToken());
}
Future<void> updateDO(DOSubmission doSubmission, DOFiles files) async {
String path = Path.join(do_files_path, user.docID);
if (files.storageFileChanged) {
if (doSubmission.storageReceiptUrl != null &&
doSubmission.storageReceiptUrl != '') {
await deleteStorageFromUrl(doSubmission.storageReceiptUrl);
}
doSubmission.storageReceiptUrl = null;
String url = await uploadStorage(path, files.storageChargeFile);
doSubmission.storageReceiptUrl = url;
}
if (files.licenseFileChanged) {
if (doSubmission.driverLicenceUrl != null &&
doSubmission.driverLicenceUrl != '') {
await deleteStorageFromUrl(doSubmission.driverLicenceUrl);
}
doSubmission.driverLicenceUrl = null;
String url = await uploadStorage(path, files.licenseFile);
doSubmission.driverLicenceUrl = url;
}
await request("/do", "PUT",
payload: doSubmission.toMap(), token: await getToken());
}
Future<void> approveDO(DOSubmission doObj) async {
await request("/do/approved", "POST",
payload: doObj.toMap(), token: await getToken());
}
Future<void> rejectDO(DOSubmission doObj) async {
await request("/do/rejected", "POST",
payload: doObj.toMap(), token: await getToken());
}
Future<void> cancelDO(DOSubmission doObj) async {
await request("/do/canceled", "POST",
payload: doObj.toMap(), token: await getToken());
}
Future<void> initDelivery(DOSubmission doObj) async {
String path = Path.join(do_files_path, user.docID);
String imgUrl =
await uploadStorage(path, doObj.driverImg, fileName: doObj.id);
doObj.driverImgUrl = imgUrl;
await request("/do/initiated", "POST",
payload: doObj.toMap(), token: await getToken());
}
Future<void> startDelivery(DOSubmission doObj) async {
await request("/do/started", "POST",
payload: doObj.toMap(), token: await getToken());
}
Future<void> endDelivery(DOSubmission doObj, Uint8List img) async {
String path = Path.join(do_files_path, user.docID);
String imgUrl = await uploadStorageData(path, img, fileName: doObj.id);
doObj.doReceiptUrl = imgUrl;
await request("/do/ended", "POST",
payload: doObj.toMap(), token: await getToken());
}
void filterData(
String status, DateTime dateTime, int _selectedIndex, int _dateIndex) {
dos.clear();
if (listener != null) {
listener.cancel();
}
this.popupMenu.index = _selectedIndex;
this.dateIndex = _dateIndex;
this.selectedDate = dateTime == null
? new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0)
: dateTime;
String path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection";
}
if (status != null && dateTime == null) {
listener = getFilterStatusSnapshot(path, status, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else if (dateTime != null && status == null) {
var endDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59);
listener =
getFilterDateSnapshot(path, 'do_date', dateTime, endDate, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else if (status != null && dateTime != null) {
var endDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59);
listener = getFilterDataSnapshot(
path, status, 'do_date', dateTime, endDate, 'do_number')
.listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else {
listener =
getQuerySnapshotByOrder(path, 'do_number').listen((snaps) async {
dos.clear();
snaps.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
}
}
addTimber(int count) {
timber = count;
notifyListeners();
}
Future<List<DOSubmission>> getDOForDelivery(DateTime dateTime) async {
List<DOSubmission> dos = [];
String path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection";
}
DateTime date = DateTime(dateTime.year, dateTime.month, dateTime.day);
DateTime dateAddOne = date.add(Duration(days: 1));
QuerySnapshot snapshots = await Firestore.instance
.collection(path)
.where("status", isEqualTo: do_approved_status)
.where("delivery_date", isGreaterThanOrEqualTo: date)
.where("delivery_date", isLessThan: dateAddOne)
.orderBy("delivery_date").orderBy("user_name")
.limit(100)
.getDocuments();
snapshots.documents.forEach((d) {
dos.add(DOSubmission.fromMap(d.data, d.documentID));
});
return dos;
}
}

View File

@@ -0,0 +1,59 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:fcs/vo/user.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class EmployeeModel extends BaseModel {
final log = Logger('EmployeeModel');
List<User> employees = [];
void initUser(user) async {
super.initUser(user);
_loadEmployees();
}
@override
logout() async {
employees = [];
}
Future<void> _loadEmployees() async {
if (!user.isOwnerAndAbove() && !user.hasAccount()) {
return;
}
try {
Firestore.instance
.collection("/$biz_collection/${setting.okEnergyId}/$user_collection")
.where("is_employee", isEqualTo: true)
.snapshots()
.listen((QuerySnapshot snapshot) {
employees.clear();
employees = snapshot.documents.map((documentSnapshot) {
var user =
User.fromMap(documentSnapshot.data, documentSnapshot.documentID);
return user;
}).toList();
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<void> updatePrivileges(String userID, List<String> privileges) async {
try {
await request("/employee/privileges", "PUT",
payload: {"id": userID, "privileges": privileges},
token: await getToken());
} catch (e) {
throw Exception(e);
}
}
}

View File

@@ -0,0 +1,269 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart';
final log = Logger('firebaseHelper');
final FirebaseAuth auth = FirebaseAuth.instance;
Future<String> getToken() async {
FirebaseUser firebaseUser = await auth.currentUser();
IdTokenResult token = await firebaseUser.getIdToken();
return token.token;
}
Stream<QuerySnapshot> getQuerySnapshot(String path) {
log.info("getQuerySnapshot Path: $path");
Stream<QuerySnapshot> snapshots =
Firestore.instance.collection(path).snapshots();
return snapshots;
}
Stream<QuerySnapshot> getQuerySnapshotByOrder(String path, String orderName) {
log.info("getQuerySnapshotByOrder Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getFilterStatusSnapshot(
String path, String searchStatus, String orderName) {
log.info("getFilterStatusSnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where('status', isEqualTo: searchStatus)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getFilterSnapshot(
String path, bool descending, String orderName) {
log.info("getFilterSnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.orderBy(orderName, descending: descending)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getDeliveryStatusSnapshot(
String path, String searchStatus, String orderName) {
log.info("getDeliveryStatusSnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where('status', isEqualTo: searchStatus)
.where("in_delivery", isEqualTo: true)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getFilterDateSnapshot(String path, String type,
DateTime startDate, DateTime endDate, String orderName) {
log.info("getFilterDateSnapshot Path: $path");
Firestore.instance
.collection(path)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.limit(1)
.getDocuments()
.then((s) {});
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getDeliveryDateSnapshot(String path, String type,
DateTime startDate, DateTime endDate, String orderName) {
log.info("getDeliveryDateSnapshot Path: $path");
Firestore.instance
.collection(path)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.where("in_delivery", isEqualTo: true)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.limit(1)
.getDocuments()
.then((s) {});
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.where("in_delivery", isEqualTo: true)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getDeliverySnapshot(String path, String type,
DateTime startDate, DateTime endDate, String orderName) {
log.info("getDeliverySnapshot Path: $path");
Firestore.instance
.collection(path)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.where("in_delivery", isEqualTo: true)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.limit(1)
.getDocuments()
.then((s) {});
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.where("in_delivery", isEqualTo: true)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getFilterDataSnapshot(String path, String status,
String type, DateTime startDate, DateTime endDate, String orderName) {
log.info("getFilterDateSnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where('status', isEqualTo: status)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getDeliveryDataSnapshot(String path, String status,
String type, DateTime startDate, DateTime endDate, String orderName) {
log.info("getDeliveryDataSnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where('status', isEqualTo: status)
.where("in_delivery", isEqualTo: true)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.orderBy(type, descending: true)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getsearchBuyerSnapshot(String path, String buyer) {
log.info("getFilterDateSnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where("user_name", isEqualTo: buyer)
.snapshots();
return snapshots;
}
Stream<DocumentSnapshot> getDocSnapshot(String path, String id) {
log.info("getDocSnapshot Path: $path, ID: $id");
Stream<DocumentSnapshot> snapshot =
Firestore.instance.collection(path).document(id).snapshots();
return snapshot;
}
Stream<QuerySnapshot> getQuerySnapshotF(String path, accountID) {
log.info("getQuerySnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where("account_id", isEqualTo: accountID)
.snapshots();
return snapshots;
}
Stream<QuerySnapshot> getFilterDateSnapshotF(String path, String accountID,
String type, DateTime startDate, DateTime endDate, String orderName) {
log.info("getFilterDateSnapshot Path: $path");
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where("account_id", isEqualTo: accountID)
.where(type, isGreaterThanOrEqualTo: startDate)
.where(type, isLessThanOrEqualTo: endDate)
.orderBy(orderName, descending: true)
.snapshots();
return snapshots;
}
Future<QuerySnapshot> getSnapshot(String path) {
log.info("getSnapshot Path: $path");
Future<QuerySnapshot> snapshots =
Firestore.instance.collection(path).getDocuments();
return snapshots;
}
Future<DocumentSnapshot> getDocSnap(String path, String id) {
log.info("getDocSnap Path: $path");
return Firestore.instance.collection(path).document(id).get();
}
Future<String> uploadStorage(String path, File file, {String fileName}) async {
if (fileName == null) {
fileName = Uuid().v4();
}
StorageReference storageReference =
FirebaseStorage.instance.ref().child('$path/$fileName');
StorageUploadTask uploadTask = storageReference.putFile(file);
await uploadTask.onComplete;
String downloadUrl = await storageReference.getDownloadURL();
print("name:${await storageReference.getName()}");
print("bucket:${await storageReference.getBucket()}");
print("path:${await storageReference.getPath()}");
print("meta:${await storageReference.getMetadata()}");
return downloadUrl;
}
Future<String> uploadStorageData(String path, Uint8List data,
{String fileName}) async {
if (fileName == null) {
fileName = Uuid().v4();
}
StorageReference storageReference =
FirebaseStorage.instance.ref().child('$path/$fileName');
StorageUploadTask uploadTask = storageReference.putData(data);
await uploadTask.onComplete;
String downloadUrl = await storageReference.getDownloadURL();
return downloadUrl;
}
Future<void> deleteStorage(String path, name) async {
try {
StorageReference storageReference =
FirebaseStorage.instance.ref().child('$path/$name');
await storageReference.delete();
} catch (e) {
log.warning("deleteStorage:$e");
}
}
Future<void> deleteStorageFromUrl(String url) async {
try {
StorageReference storageReference =
await FirebaseStorage.instance.getReferenceFromUrl(url);
await storageReference.delete();
} catch (e) {
log.warning("deleteStorage:$e");
}
}

View File

@@ -0,0 +1,45 @@
import 'package:flutter/painting.dart';
import 'package:fcs/model/base_model.dart';
import 'package:fcs/model/shared_pref.dart';
import 'package:fcs/vo/setting.dart';
import 'package:fcs/widget/localization/transalation.dart';
class LanguageModel extends BaseModel {
String language;
bool isEng = true;
static final List<String> languageCodesList =
Translation().supportedLanguagesCodes;
static final List<String> languagesList = Translation().supportedLanguages;
final Map<dynamic, dynamic> languagesMap = {
languagesList[0]: languageCodesList[0],
languagesList[1]: languageCodesList[1],
};
void initSetting(Setting setting) async {
this.language = await load();
this.isEng = this.language == "English";
Translation().onLocaleChanged(Locale(languagesMap[language]));
notifyListeners();
}
@override
logout() async {
}
Future<String> load() async {
var data =await SharedPref.getLang();
if (data == null) return languagesList[1];
return data;
}
void saveLanguage(String language) async {
Translation().onLocaleChanged(Locale(languagesMap[language]));
SharedPref.saveLang(language);
this.language = language;
this.isEng = this.language == "English";
notifyListeners();
}
}

59
lib/model/log_model.dart Normal file
View File

@@ -0,0 +1,59 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/vo/document_log.dart';
import 'package:fcs/vo/log.dart';
import 'base_model.dart';
import 'constants.dart';
class LogModel extends BaseModel {
List<Log> logs = [];
List<DocLog> docLogs = [];
void initUser(user) {
super.initUser(user);
_loadLogs();
}
@override
logout() async {
logs = [];
docLogs=[];
}
Future<void> _loadLogs() async {
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(
"/$biz_collection/${setting.okEnergyId}/$user_collection/${user.docID}/$log_collection")
.orderBy("active_time", descending: true)
.limit(30)
.snapshots();
snapshots.listen((snaps) async {
logs = snaps.documents
.map((documentSnapshot) =>
Log.fromMap(documentSnapshot.data, documentSnapshot.documentID))
.toList();
notifyListeners();
});
}
Future<void> loadDocLogs(String docID) async {
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection("/$biz_collection/${setting.okEnergyId}/$log_collection")
.where('doc_id', isEqualTo: docID)
.orderBy('date', descending: true)
.limit(30)
.snapshots();
snapshots.listen((snaps) async {
docLogs.clear();
docLogs = snaps.documents
.map((documentSnapshot) => DocLog.fromMap(
documentSnapshot.data, documentSnapshot.documentID))
.toList();
notifyListeners();
});
}
}

450
lib/model/main_model.dart Normal file
View File

@@ -0,0 +1,450 @@
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:device_info/device_info.dart';
import 'package:dio/dio.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:package_info/package_info.dart';
import 'package:path/path.dart' as Path;
import 'package:fcs/model/shared_pref.dart';
import 'package:fcs/util.dart';
import 'package:fcs/vo/bank_account.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/vo/setting.dart';
import 'package:fcs/widget/NetworkConnectivity.dart';
import '../config.dart';
import '../vo/status.dart';
import '../vo/user.dart';
import 'api_helper.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class ImplementInterfaceModel {
void initUser(User user) => {};
void initSetting(Setting setting) => {};
}
class MainModel extends ChangeNotifier {
final log = Logger('MainModel');
final FirebaseAuth auth = FirebaseAuth.instance;
FirebaseMessaging firebaseMessaging;
List<BaseModel> models = [];
User user;
Buyer buyer;
FirebaseUser firebaseUser;
StreamSubscription<DocumentSnapshot> userListener;
StreamSubscription<DocumentSnapshot> buyerListener;
bool pinRequired;
Timer pinTimer;
Setting setting;
PackageInfo packageInfo;
bool isLoaded = true;
bool isOnline = true;
static const PIN_TIME_MIN = 10;
MainModel() {
// NetworkConnectivity.instance.statusStream.listen((data) {
// bool _isOnline = data["isOnline"];
// if (_isOnline && !this.isOnline) {
// init();
// }
// this.isOnline = _isOnline;
// notifyListeners();
// });
}
resetPinTimer() {
if (pinTimer != null && pinTimer.isActive) {
pinTimer.cancel();
}
pinRequired = false;
pinTimer = Timer(Duration(minutes: PIN_TIME_MIN), () {
pinRequired = true;
});
}
bool isLogin() {
return true;
}
bool hasEmail() {
return this.user != null && this.user.isEmail();
}
bool agreedTerm() {
return this.user != null && this.user.agreeTerms;
}
bool isBuyer() {
return this.user == null || this.user.isBuyer();
}
bool isRegBuyer() {
return isBuyer() && buyer != null;
}
bool isApprovedBuyer() {
return isBuyer() && buyer != null && buyer.isApproved();
}
bool isSysAdmin() {
return this.user != null && this.user.isSysAdmin();
}
bool isSysSupport() {
return this.user != null && this.user.isSysSupport();
}
bool isBizAdmin() {
return this.user != null && this.user.isBizAdmin();
}
bool isOwnerAndAbove() {
return this.user != null && this.user.isOwnerAndAbove();
}
bool isAdmin() {
return this.user != null && this.user.hasAdmin();
}
bool showHistoryBtn() {
return isSysAdmin() || isSysSupport() || isBizAdmin();
}
init() async {
// await _loadSetting();
// _loadUser();
// resetPinTimer();
}
void addModel(BaseModel model) {
models.add(model);
model.mainModel = this;
}
void _initUser(User user) {
models.forEach((m) => m.initUser(user));
if (firebaseMessaging != null) {
firebaseMessaging.subscribeToTopic(user.docID);
}
}
void _initSetting(Setting setting) {
models.forEach((m) => m.initSetting(setting));
}
Future<void> _loadSetting() async {
this.setting = await _getSetting();
this.packageInfo = await PackageInfo.fromPlatform();
_initSetting(setting);
}
void _loadUser() async {
this.firebaseUser = await auth.currentUser();
if (this.firebaseUser == null) {
this.isLoaded = true;
notifyListeners();
return;
}
_logUser(this.firebaseUser);
// load from local, if successful,notify listeners
User _user = await SharedPref.getUser();
if (_user != null) {
await _user.setFirebaseUser(this.firebaseUser);
_initUser(_user);
this.user = _user;
if (this.user.isRegisteredBuyer()) {
_loadBuyer();
}
this.isLoaded = true;
notifyListeners();
log.info("user loaded from shared pref!");
}
_listenUser();
}
void _listenUser() {
if (this.userListener != null) {
this.userListener.cancel();
}
this.userListener = getDocSnapshot(
"/$biz_collection/${setting.okEnergyId}/$user_collection",
firebaseUser.uid)
.listen((userSnap) async {
if (userSnap.exists) {
User _user = User.fromMap(userSnap.data, userSnap.documentID);
// load claims
try {
FirebaseUser _firebaseUser = await getProfile(this.firebaseUser);
await _user.setFirebaseUser(_firebaseUser);
_initUser(_user);
this.user = _user;
this.firebaseUser = _firebaseUser;
await SharedPref.saveUser(this.user);
} catch (e) {
log.warning(e.toString());
}
log.info(
"_loadUser => ID: ${this.user.docID}, AccountID: ${this.user.accountID},"
"BizID: ${this.user.accountID},"
", Privileges: ${this.user.claimPrivileges}, isSysAdmin: ${this.user.isSysAdmin()}");
if (this.user.isRegisteredBuyer()) {
_loadBuyer();
}
this.isLoaded = true;
notifyListeners();
}
});
}
void _loadBuyer() async {
if (this.user == null) return;
if (buyerListener != null) buyerListener.cancel();
buyerListener = getDocSnapshot(
"/$biz_collection/${setting.okEnergyId}/$buyer_collection",
this.user.docID)
.listen((buyerSnap) async {
if (buyerSnap.exists) {
this.buyer = Buyer.fromMap(buyerSnap.data, buyerSnap.documentID);
} else {
this.buyer = null;
}
notifyListeners();
});
}
@override
void dispose() {
// super.dispose();
// if (this.userListener != null) {
// this.userListener.cancel();
// }
// SharedPref.removeUser();
// this.user = User();
}
Future<void> login(String phoneNumber, String pass) async {
var id = phoneNumber.replaceFirst("+", "");
id = updatePhoneNumber(id);
var data = {"id": id, "password": pass};
var result = await requestAPI("/login", "POST", payload: data);
var token = result["Token"];
// login with custom token
AuthResult r = await this.auth.signInWithCustomToken(token: token);
this.firebaseUser = r.user;
isLoaded = false;
_loadUser();
_logUser(this.firebaseUser);
}
Future<FirebaseUser> getProfile(FirebaseUser firebaseUser) async {
IdTokenResult idtoken = await firebaseUser.getIdToken();
var data = await requestAPI(
"/profile",
"GET",
token: idtoken.token,
);
var _token = data["Token"];
AuthResult a = await this.auth.signInWithCustomToken(token: _token);
return a.user;
}
Future<void> _logUser(FirebaseUser firebaseUser) async {
IdTokenResult idtoken = await firebaseUser.getIdToken();
await requestAPI(
"/log",
"GET",
token: idtoken.token,
);
}
Future<void> logout() async {
if (this.userListener != null) {
await this.userListener.cancel();
}
await auth.signOut();
this.user = null;
this.buyer = null;
this.firebaseUser = null;
await SharedPref.removeUser();
if (firebaseMessaging != null) {
firebaseMessaging.unsubscribeFromTopic(user.docID);
}
// logout models
models.forEach((m) => m.logout());
notifyListeners();
}
Future<void> signup(
String name, password, confirmPassword, phoneNumber) async {
if (password == "" || password.length < 6) {
throw Exception("Password must be at least 6 characters");
}
if (password != confirmPassword) {
throw Exception("Password mismatch");
}
var id = phoneNumber.replaceFirst("+", "");
id = updatePhoneNumber(id);
var inputData = {"id": id, "password": password, "user_name": name};
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
String deviceName = "${androidInfo.model}(${androidInfo.id})";
var url = "${Config.instance.apiURL}/signup";
Response response = await Dio().post(url,
data: inputData,
options: Options(
headers: {"Device": androidInfo.androidId + ":" + deviceName}));
var data = Status.fromJson(response.data);
if (data.status != 'Ok') {
throw Exception("${data.errorCode} : ${data.message}");
}
}
Future<void> confirmSignup(
String phoneNumber, password, confirmSMSCode) async {
var id = phoneNumber.replaceFirst("+", "");
id = updatePhoneNumber(id);
if (confirmSMSCode == "" || confirmSMSCode.length != 6) {
throw Exception("Password must be 6 characters");
}
var inputData = {
"id": id,
"password": password,
"confirmation_code": confirmSMSCode
};
var url = "${Config.instance.apiURL}/confirm";
Response response = await Dio().post(
url,
data: inputData,
);
var data = Status.fromJson(response.data);
if (data.status != 'Ok') {
throw Exception(data.message);
}
}
bool isSupport() {
if (packageInfo == null || setting == null) return false;
return int.parse(packageInfo.buildNumber) >= setting.supportBuildNum;
}
Future<Setting> _getSetting() async {
var snap = await Firestore.instance
.collection(config_collection)
.document(setting_doc_id)
.get();
if (!snap.exists) {
return null;
}
_listSetting();
return Setting.fromMap(snap.data);
}
void _listSetting() {
getDocSnapshot("/configs", setting_doc_id).listen((snap) {
this.setting = Setting.fromMap(snap.data);
notifyListeners();
});
}
Future<void> updateProfile(String name) async {
await requestAPI("/user", "PUT",
payload: {"user_name": name}, token: await getToken());
}
Future<void> updateTerms(String terms) async {
await requestAPI("/terms", "PUT",
payload: {"terms": terms}, token: await getToken());
}
Future<void> agreeTerms() async {
await requestAPI("/user/agree", "PUT", token: await getToken());
}
Future<void> updateContact(Setting setting) async {
await requestAPI("/contact", "PUT",
payload: {
'email': setting.email,
'facebook_url': setting.facebook,
'web_url': setting.website,
'phones': setting.phones,
'bank_account_info': setting.bankAccountInfo,
'delivery_phone': setting.deliveryPhone,
'address': setting.address,
},
token: await getToken());
}
Future<void> updateSetting(Setting setting) async {
await requestAPI("/setting", "PUT",
payload: {
'do_expire_hours': setting.doExpireInHours,
'po_expire_hours': setting.poExpireInHours,
'po_open_at': setting.poOpenAt,
'po_close_at': setting.poCloseAt,
'po_close_on': setting.poCloseOn,
'first_storage_charge_in': setting.firstStorageChargeIn,
'first_storage_charge': setting.firstStorageCharge,
'second_storage_charge_in': setting.secondStorageChargeIn,
'second_storage_charge': setting.secondStorageCharge,
'latest_delivery_days': setting.latestDeliveryDay,
},
token: await getToken());
}
Future<void> addBankAccount(BankAccount bankAccount, File image) async {
String url = await uploadStorage(bank_images_path, image);
bankAccount.bankLogo = url;
await requestAPI("/bank_accounts", "POST",
payload: bankAccount.toMap(), token: await getToken());
}
Future<void> updateBankAccount(BankAccount bankAccount, File image) async {
if (image != null) {
String url = await uploadStorage(bank_images_path, image);
bankAccount.bankLogo = url;
}
await requestAPI("/bank_accounts", "PUT",
payload: bankAccount.toMap(), token: await getToken());
}
Future<void> deleteBankAccount(BankAccount bankAccount) async {
await requestAPI("/bank_accounts", "DELETE",
payload: bankAccount.toMap(), token: await getToken());
}
}

234
lib/model/manual_model.dart Normal file
View File

@@ -0,0 +1,234 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:archive/archive_io.dart';
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:fcs/model/firebase_helper.dart';
import 'package:fcs/vo/manual.dart';
import 'package:fcs/vo/setting.dart';
import 'package:path/path.dart' as Path;
import 'base_model.dart';
typedef void SlideDataCallback();
class ManualModel extends BaseModel {
final log = Logger('ManualModel');
List<ManualItem> helps = [];
List<ManualItem> manuals = [];
String version;
SlideDataCallback slideDataCallback;
List deleteImage = [];
String dataDir;
void initSetting(Setting setting) async {
_download(setting);
super.initSetting(setting);
}
@override
logout() async {}
Future<void> _download(Setting setting) async {
this.dataDir = (await getApplicationDocumentsDirectory()).path;
version = setting.helpVersion;
var file = File('$dataDir/${setting.helpFileName()}');
if (await file.exists()) {
_loadJsonData();
return;
}
String url = setting.helpURL;
var req = await http.Client().get(Uri.parse(url));
File zippedFile = await file.writeAsBytes(req.bodyBytes);
File prev = File('$dataDir/manual');
if (await prev.exists()) {
await prev.delete(recursive: true);
}
var bytes = zippedFile.readAsBytesSync();
var archive = ZipDecoder().decodeBytes(bytes);
for (var file in archive) {
var fileName = '$dataDir/manual/${file.name}';
if (file.isFile) {
var outFile = File(fileName);
outFile = await outFile.create(recursive: true);
await outFile.writeAsBytes(file.content);
}
}
_loadJsonData();
}
List<ManualItem> getHelpList(bool isBuyer) {
return helps.where((h) => isBuyer ? h.isBuyer : true).toList();
}
getSlideList(int manIndex) {
var slides = helps[manIndex].slides;
return slides;
}
getSlideData(int manIndex, int slideIndex) {
var slide;
slide = helps[manIndex].slides[slideIndex];
return slide;
}
Future<void> _loadJsonData() async {
try {
final directory = await getApplicationDocumentsDirectory();
File file = File('${directory.path}/manual/manual.json');
String contents = await file.readAsString();
var convertArray = jsonDecode(contents);
manuals.clear();
convertArray.forEach((item) {
var _list = ManualItem.fromMap(item);
manuals.add(_list);
});
} catch (e) {
log.warning("Error:${e.toString()}");
}
helps.clear();
helps = manuals.map((e) => ManualItem.clone(e)).toList();
}
addManualTitle(ManualItem manualItem) {
helps.add(manualItem);
notifyListeners();
}
uploadStorageManualData(String version, String dir) async {
String fileName = 'help-v$version.zip';
var converthelps = [];
for (final converthelp in helps) {
converthelps.add(converthelp.toJson());
}
var json = jsonEncode(converthelps);
Directory myDir = new Directory('$dir/manual/img');
List _images;
_images = myDir.listSync(recursive: true, followLinks: false);
var newImgData = await convertArchiveImgFile(_images, dir, json);
File updateImgFile = File('$dir/manual/update');
updateImgFile.writeAsBytesSync(newImgData);
var bytes = updateImgFile.readAsBytesSync();
uploadDataZip(bytes, fileName);
}
convertArchiveImgFile(List imgList, String dataDir, json) async {
File file = new File('$dataDir/update');
if (await file.exists()) {
await file.delete(recursive: true);
}
Archive zipArchive = new Archive();
List<int> utf8encoded = utf8.encode(json);
ArchiveFile jsonFile =
new ArchiveFile("manual.json", utf8encoded.length, utf8encoded);
zipArchive.addFile(jsonFile);
for (var img in imgList) {
String basename = Path.basename(img.path);
if (deleteImage.length != 0) {
for (var dImgName in deleteImage) {
if (dImgName != basename) {
Uint8List bytesPhoto = img.readAsBytesSync() as Uint8List;
ArchiveFile jsonFile =
new ArchiveFile("img/$basename", bytesPhoto.length, bytesPhoto);
zipArchive.addFile(jsonFile);
}
}
} else {
Uint8List bytesPhoto = img.readAsBytesSync() as Uint8List;
ArchiveFile jsonFile =
new ArchiveFile("img/$basename", bytesPhoto.length, bytesPhoto);
zipArchive.addFile(jsonFile);
}
}
List<int> zipInBytes = new ZipEncoder().encode(zipArchive);
file.writeAsBytesSync(zipInBytes);
var bytes = file.readAsBytesSync();
return bytes;
}
uploadDataZip(Uint8List data, String fileName) async {
String path = '/ok/img';
String url = await uploadStorageData(path, data, fileName: fileName);
}
resetManualItems() {
helps.clear();
//clone manauals
helps = manuals.map((p) => ManualItem.clone(p)).toList();
// return helps.where((h) => isBuyer ? h.isBuyer : true).toList();
}
saveInstruction(int manIndex, int slideIndex, int instIndex,
Instruction instruction, Instruction oldInst, bool isEng) {
if (isEng) {
var inst = helps[manIndex].slides[slideIndex].instructions.toList();
instruction.id = oldInst.id;
if (inst.length != 0) {
helps[manIndex].slides[slideIndex].instructions.remove(oldInst);
helps[manIndex]
.slides[slideIndex]
.instructions
.insert(instIndex, instruction);
} else {
helps[manIndex].slides[slideIndex].instructions.add(instruction);
}
} else {
var inst = helps[manIndex].slides[slideIndex].instructionsmm.toList();
instruction.id = oldInst.id;
if (inst.length != 0) {
helps[manIndex].slides[slideIndex].instructionsmm.remove(oldInst);
helps[manIndex]
.slides[slideIndex]
.instructionsmm
.insert(instIndex, instruction);
} else {
helps[manIndex].slides[slideIndex].instructionsmm.add(instruction);
}
}
notifyListeners();
}
saveSlideData(int manIndex, int slideIndex, SlideData slideData) {
helps[manIndex].slides.add(slideData);
notifyListeners();
}
changeSlideImage(int manIndex, int slideIndex, SlideData slideData) {
var oldSlide = helps[manIndex].slides[slideIndex];
helps[manIndex].slides.remove(oldSlide);
helps[manIndex].slides.insert(slideIndex, slideData);
notifyListeners();
}
deleteSlideData(int manIndex, SlideData slideData) {
String engImage = slideData.image;
String mmImage = slideData.imagemm;
deleteImage.add(mmImage);
deleteImage.add(engImage);
helps[manIndex].slides.remove(slideData);
notifyListeners();
}
deleteManualItem(ManualItem item) {
// if(helps.isEmpty){
// helps = new List();
// }
helps.remove(item);
notifyListeners();
}
}

35
lib/model/messaging.dart Normal file
View File

@@ -0,0 +1,35 @@
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:logging/logging.dart';
import 'package:fcs/vo/user.dart';
class MessagingFCM {
final log = Logger('MessagingFCM');
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
MessagingFCM(User user) {
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
log.info("onMessage: $message");
},
// onBackgroundMessage: backgroundMessageHandler,
onLaunch: (Map<String, dynamic> message) async {
log.info("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
log.info("onResume: $message");
},
);
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(
sound: true, badge: true, alert: true, provisional: true));
_firebaseMessaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
log.info("Settings registered: $settings");
});
_firebaseMessaging.getToken().then((String token) {
log.info("Token:$token");
});
_firebaseMessaging.subscribeToTopic(user.docID);
}
}

View File

@@ -0,0 +1,74 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/vo/notification.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class NotificationModel extends BaseModel {
int filer=0;
List<Notification> notifications = [];
var filterValues = {1: "po", 2: "do", 3: "buyer"};
List<Notification> get notis {
return notifications
.where((n) => filer == 0 || n.itemType == filterValues[filer])
.toList();
}
int unseen = 0;
void initUser(user) {
super.initUser(user);
_loadNotifications();
}
@override
logout() async {
notifications = [];
}
Future<void> _loadNotifications() async {
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(
"/$biz_collection/${setting.okEnergyId}/$user_collection/${user.docID}/$notification_collection")
.orderBy("time", descending: true)
.limit(50)
.snapshots();
snapshots.listen((snaps) async {
notifications.clear();
unseen = 0;
// snaps.documentChanges.forEach((c) {
// if (c.type == DocumentChangeType.added) {
// FlutterRingtonePlayer.play(
// android: AndroidSounds.notification,
// ios: IosSounds.glass,
// );
// }
// });
snaps.documents.forEach((d) {
var n = Notification.fromMap(d.data, d.documentID);
if (!n.seen) unseen++;
notifications.add(n);
});
notifyListeners();
});
}
void filter(int filter) {
this.filer = filter;
}
Future<void> seen() async {
await request("/notification/seen", "POST", token: await getToken());
}
Future<void> seenID(String id) async {
await request("/notification/seen/${user.docID}/$id", "POST",
token: await getToken());
}
}

View File

@@ -0,0 +1,4 @@
class Listener {
}

88
lib/model/pd_model.dart Normal file
View File

@@ -0,0 +1,88 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:fcs/model/constants.dart';
import 'package:fcs/vo/pd.dart';
import 'package:fcs/vo/user.dart';
import 'base_model.dart';
import 'firebase_helper.dart';
class PDModel extends BaseModel {
final log = Logger('PDModel');
List<PD> pds = [];
int dateIndex = 0;
DateTime selectedDate = DateTime.now();
void initUser(User user) async {
super.initUser(user);
loadPDs();
}
@override
logout() async {
pds = [];
}
loadPDs() {
if (!user.isOwnerAndAbove() && !user.hasInventory()) {
return;
}
try {
String path = "/$biz_collection/${setting.okEnergyId}/$pds_collection";
var startDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0);
var endDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 23, 59, 59);
pds.clear();
getFilterDateSnapshot(path, 'date', startDate, endDate, 'pd_number')
.listen((QuerySnapshot snapshot) {
pds = snapshot.documents.map((documentSnapshot) {
var data =
PD.fromMap(documentSnapshot.data, documentSnapshot.documentID);
return data;
}).toList();
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<PD> loadPDLines(PD pd) async {
var snaps = await getSnapshot(
"/$biz_collection/${setting.okEnergyId}/$pds_collection/${pd.id}/$product_collection");
pd.pdLines = snaps.documents.map((s) => PDLine.fromMap(s.data)).toList();
return pd;
}
Future<void> createPD(PD pd) async {
await request("/pd", "POST",
payload: pd.toMap(), token: await getToken());
}
void filterDate(DateTime dateTime, int _dateIndex) {
this.selectedDate = dateTime;
this.dateIndex = _dateIndex;
String path = "/$biz_collection/${setting.okEnergyId}/$pds_collection";
var endDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59);
pds.clear();
getFilterDateSnapshot(path, 'date', dateTime, endDate, 'pd_number').listen(
(snaps) {
pds = snaps.documents.map((documentSnapshot) {
var data =
PD.fromMap(documentSnapshot.data, documentSnapshot.documentID);
return data;
}).toList();
notifyListeners();
}, onError: (error) {
log.warning("FIRESTORE ERROR>>$error");
});
}
}

294
lib/model/po_model.dart Normal file
View File

@@ -0,0 +1,294 @@
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as Path;
import 'package:fcs/model/constants.dart';
import 'package:fcs/pages/po/po_files.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/po.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class POSubmissionModel extends BaseModel {
final log = Logger('POSubmissionModel');
StreamSubscription<QuerySnapshot> listener;
List<POSubmission> pos = [];
List<POSubmission> approvedPOs = [];
PopupMenu popupMenu = new PopupMenu(index: 0);
int dateIndex = 0;
DateTime selectedDate = DateTime.now();
void initUser(user) async {
super.initUser(user);
_loadPOs();
_loadApprovedPOs();
}
@override
logout() async {
if (listener != null) await listener.cancel();
pos = [];
approvedPOs = [];
}
Future<void> _loadPOs() async {
String path;
if (user.hasPO() || user.isOwnerAndAbove()) {
path = "/$biz_collection/${setting.okEnergyId}/$pos_collection";
}
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$pos_collection";
}
var startDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0);
var endDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 23, 59, 59);
listener =
getFilterDateSnapshot(path, 'po_date', startDate, endDate, 'po_number')
.listen((snaps) async {
pos.clear();
snaps.documents.forEach((d) {
pos.add(POSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
}
Future<void> _loadApprovedPOs() async {
if (!user.isBuyer()) {
return;
}
approvedPOs.clear();
String path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$pos_collection";
var docs = await Firestore.instance
.collection(path)
.where("status", isEqualTo: po_approved_status)
.orderBy("po_approved_date", descending: false)
.limit(1)
.getDocuments();
Firestore.instance
.collection(path)
.where("status", isEqualTo: po_approved_status)
.orderBy("po_approved_date", descending: false)
.limit(10)
.snapshots(includeMetadataChanges: true)
.listen((snaps) async {
List<POSubmission> _approved = [];
for (var d in snaps.documents) {
if (d.metadata.isFromCache) continue;
var po = POSubmission.fromMap(d.data, d.documentID);
po.poLines = await loadPOLines(po.id);
_approved.add(po);
}
approvedPOs.clear();
approvedPOs.addAll(_approved);
notifyListeners();
});
}
Future<POSubmission> getPO(String id) async {
String path = "/$biz_collection/${setting.okEnergyId}/$pos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$pos_collection";
}
var poSnap = await getDocSnap(path, id);
return POSubmission.fromMap(poSnap.data, poSnap.documentID);
}
Future<List<POLine>> loadPOLines(String poID) async {
String path =
"/$biz_collection/${setting.okEnergyId}/$pos_collection/$poID/$po_product_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$pos_collection/$poID/$product_collection";
}
var snaps = await Firestore.instance.collection(path).getDocuments();
List<POLine> poLines =
snaps.documents.map((s) => POLine.fromMap(s.data)).toList();
return poLines;
}
Future<POSubmission> loadDOs(POSubmission po) async {
String path = "/$biz_collection/${setting.okEnergyId}/$dos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection";
}
var snaps = await Firestore.instance
.collection(path)
.where("po_number", isEqualTo: po.poNumber)
.orderBy('do_number', descending: true)
.getDocuments();
po.dos = snaps.documents
.map((s) => DOSubmission.fromMap(s.data, s.documentID))
.toList();
return po;
}
Future<DOSubmission> loadDOLines(DOSubmission doSub) async {
String path =
"/$biz_collection/${setting.okEnergyId}/$dos_collection/${doSub.id}/$product_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$dos_collection/${doSub.id}/$product_collection";
}
var snaps = await getSnapshot(path);
doSub.doLines = snaps.documents.map((s) => DOLine.fromMap(s.data)).toList();
return doSub;
}
Future<void> createPO(POSubmission po, List<File> files) async {
if (files != null) {
if (files.length > 5) throw Exception("Exceed number of file upload");
po.poReceiptUrls = [];
for (File f in files) {
String path = Path.join(po_files_path, user.docID);
String url = await uploadStorage(path, f);
po.poReceiptUrls.add(url);
}
}
await request("/po", "POST", payload: po.toMap(), token: await getToken());
}
Future<void> updatePO(
POSubmission po, List<File> files, List<String> deletedUrls) async {
if (deletedUrls != null)
for (String url in deletedUrls) {
po.poReceiptUrls.remove(url);
await deleteStorageFromUrl(url);
}
if (files != null) {
if (files.length + po.poReceiptUrls.length > 5)
throw Exception("Exceed number of file upload");
po.poReceiptUrls = po.poReceiptUrls == null ? [] : po.poReceiptUrls;
for (File f in files) {
String path = Path.join(po_files_path, user.docID);
String url = await uploadStorage(path, f);
po.poReceiptUrls.add(url);
}
}
await request("/po", "PUT", payload: po.toMap(), token: await getToken());
}
Future<void> approvePO(POSubmission po) async {
await request("/po/approved", "POST",
payload: po.toMap(), token: await getToken());
}
Future<void> rejectPO(POSubmission po) async {
await request("/po/rejected", "POST",
payload: po.toMap(), token: await getToken());
}
Future<void> cancelPO(POSubmission po) async {
await request("/po/canceled", "POST",
payload: po.toMap(), token: await getToken());
}
void filterData(
String status, DateTime dateTime, int _selectedStatus, int _dateIndex) {
pos.clear();
var startDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 0, 0, 0);
var endDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59);
if (this.listener != null) {
this.listener.cancel();
}
this.popupMenu.index = _selectedStatus;
this.dateIndex = _dateIndex;
this.selectedDate = dateTime == null
? new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0)
: dateTime;
String path = "/$biz_collection/${setting.okEnergyId}/$pos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$pos_collection";
}
if (status != null && dateTime == null) {
this.listener = getFilterStatusSnapshot(path, status, 'po_number')
.listen((snaps) async {
pos.clear();
snaps.documents.forEach((d) {
pos.add(POSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else if (dateTime != null && status == null) {
this.listener = getFilterDateSnapshot(
path, 'po_date', startDate, endDate, 'po_number')
.listen((snaps) async {
pos.clear();
snaps.documents.forEach((d) {
pos.add(POSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else if (status != null && dateTime != null) {
this.listener = getFilterDataSnapshot(
path, status, 'po_date', startDate, endDate, 'po_number')
.listen((snaps) {
pos.clear();
snaps.documents.forEach((d) {
pos.add(POSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} else {
this.listener =
getQuerySnapshotByOrder(path, 'po_number').listen((snaps) async {
pos.clear();
snaps.documents.forEach((d) {
pos.add(POSubmission.fromMap(d.data, d.documentID));
});
notifyListeners();
});
}
}
Future<List<POSubmission>> getPOForRevenue(DateTime dateTime) async {
List<POSubmission> pos = [];
String path = "/$biz_collection/${setting.okEnergyId}/$pos_collection";
if (user.isBuyer()) {
path =
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$pos_collection";
}
DateTime date = DateTime(dateTime.year, dateTime.month, dateTime.day);
DateTime dateAddOne = date.add(Duration(days: 1));
QuerySnapshot snapshots = await Firestore.instance
.collection(path)
.where('status', whereIn: [po_approved_status, po_closed_status])
.where("po_approved_date", isGreaterThanOrEqualTo: date)
.where("po_approved_date", isLessThan: dateAddOne)
.orderBy("po_approved_date")
.orderBy("user_name")
.limit(100)
.getDocuments();
snapshots.documents.forEach((d) {
pos.add(POSubmission.fromMap(d.data, d.documentID));
});
return pos;
}
}

View File

@@ -0,0 +1,119 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:fcs/vo/product.dart';
import 'package:fcs/vo/setting.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class ProductModel extends BaseModel {
final log = Logger('ProductModel');
List<Product> products = [];
List<Product> tempProducts = [];
List<ProductPrice> get getPrices {
List<ProductPrice> productPrices = [];
this.products.forEach((p) => productPrices.addAll(p.getPrices));
productPrices.sort((p1, p2) => p1.compareTo(p2));
return productPrices;
}
String getProductName(String productID) {
return products.firstWhere((p) => p.id == productID).name;
}
Product getProduct(String productID) {
return products.firstWhere((p) => p.id == productID);
}
List<Product> get productsToEdit {
// clone products
tempProducts = products.map((p) => Product.clone(p)).toList();
tempProducts.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
// set old price with price
tempProducts.forEach((p) {
p.oldPirce = p.price;
p.action = "update";
});
return tempProducts;
}
void saveProduct(Product product, String name, price, displayOrder, int color,
bool isDisable) {
if (product == null) {
tempProducts.add(Product(
action: "create",
name: name,
price: int.parse(price),
color: color,
displayOrder: int.parse(displayOrder),
isDisable: isDisable));
} else {
Product _product = product;
_product.name = name;
_product.price = int.parse(price);
_product.color = color;
_product.displayOrder = int.parse(displayOrder);
_product.isDisable = isDisable;
if (_product.id == null) {
_product.action = "create";
} else {
_product.action = "update";
}
}
notifyListeners();
}
void deleteProduct(Product product) {
if (product == null) {
return;
}
if (product.id == null) {
tempProducts.remove(product);
} else {
Product _product = tempProducts.firstWhere((p) => p.id == product.id);
_product.action = "delete";
}
notifyListeners();
}
@override
void initSetting(Setting setting) {
super.initSetting(setting);
try {
getQuerySnapshot(
"/$biz_collection/${setting.okEnergyId}/$product_collection")
.listen((QuerySnapshot snapshot) {
products.clear();
products = snapshot.documents.map((documentSnapshot) {
var data = Product.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return data;
}).toList();
products.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
@override
logout() async {
tempProducts = [];
}
updateProducts(List<Product> products) async {
var items = [];
products.forEach((p) {
if (p.action != null) items.add(p.toMap());
});
await request("/products", "PUT", payload: items, token: await getToken());
}
}

86
lib/model/reg_model.dart Normal file
View File

@@ -0,0 +1,86 @@
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:path/path.dart' as Path;
import 'package:fcs/vo/buyer.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class Attachments {
File nricFront, nricBack;
}
class RegModel extends BaseModel {
Buyer reg = Buyer();
bool isLoaded = false;
StreamSubscription regListener;
void initUser(user) {
super.initUser(user);
if (user.isRegisteredBuyer()) {
_loadReg();
} else {
reg = Buyer();
}
}
@override
logout() async {
if (regListener != null) await regListener.cancel();
reg = Buyer();
}
Future<void> _loadReg() async {
if (regListener != null) {
regListener.cancel();
}
regListener = getDocSnapshot(
"/$biz_collection/${setting.okEnergyId}/$buyer_collection",
"${user.docID}")
.listen((snap) async {
if (snap.exists) {
reg = Buyer.fromMap(snap.data, snap.documentID);
QuerySnapshot q = await getSnapshot(
"/$biz_collection/${setting.okEnergyId}/$buyer_collection/${user.docID}/$product_collection");
reg.buyerProducts.clear();
q.documents.forEach((d) {
reg.buyerProducts.add(BuyerProduct.fromMap(d.data, d.documentID));
});
} else {
reg = Buyer();
}
isLoaded = true;
notifyListeners();
});
}
Future<void> register(Buyer buyer, Attachments attachments) async {
String path = Path.join(reg_files_path, user.docID);
String urlFront = await uploadStorage(path, attachments.nricFront);
buyer.nricFrontUrl = urlFront;
String urlBack = await uploadStorage(path, attachments.nricBack);
buyer.nricBackUrl = urlBack;
await request("/reg", "POST",
payload: buyer.toMap(), token: await getToken());
}
Future<void> update(Buyer buyer, Attachments attachments) async {
String path = Path.join(reg_files_path, user.docID);
if (attachments.nricFront != null) {
String urlFront = await uploadStorage(path, attachments.nricFront);
buyer.nricFrontUrl = urlFront;
}
if (attachments.nricBack != null) {
String urlBack = await uploadStorage(path, attachments.nricBack);
buyer.nricBackUrl = urlBack;
}
await request("/buyer/update", "PUT",
payload: buyer.toMap(), token: await getToken());
}
}

700
lib/model/report_model.dart Normal file
View File

@@ -0,0 +1,700 @@
import 'dart:async';
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/vo/report.dart';
import 'package:fcs/vo/report_user.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:fcs/model/api_helper.dart' as api;
import 'package:fcs/vo/report.dart';
import '../config.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
import 'shared_pref.dart';
class ReportModel extends BaseModel {
StreamSubscription<QuerySnapshot> listener;
StreamSubscription<QuerySnapshot> listenerUser;
List<Report> reports = [];
List<Report> userReports = [];
List filterValue = [];
List<Report> get reportList {
List<Report> _reports = [];
if (user.isOwnerAndAbove() || user.hasAdmin()) {
_reports = reports;
} else {
_reports.addAll(reports);
userReports.forEach((r) {
if (!_reports.contains(r)) {
_reports.add(r);
}
});
}
_reports.sort((a, b) => a.display.compareTo(b.display));
return _reports;
}
void initUser(user) {
super.initUser(user);
reports = [];
_loadReports();
}
Future<void> _loadReports() async {
if (listener != null) listener.cancel();
if (listenerUser != null) listenerUser.cancel();
if (user.isOwnerAndAbove() || user.hasAdmin()) {
listener = Firestore.instance
.collection("/$reports_collection")
.snapshots()
.listen((snaps) async {
reports.clear();
reports = snaps.documents.map((documentSnapshot) {
var report = Report.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return report;
}).toList();
notifyListeners();
});
} else {
listener = Firestore.instance
.collection("/$reports_collection")
.where("for_all_users", isEqualTo: true)
.snapshots()
.listen((snaps) async {
reports.clear();
reports = snaps.documents.map((documentSnapshot) {
var report = Report.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return report;
}).toList();
notifyListeners();
});
listenerUser = Firestore.instance
.collection("/$report_user_collection")
.where("user_id", isEqualTo: user.docID)
.snapshots()
.listen((snaps) async {
userReports.clear();
userReports = snaps.documents.map((documentSnapshot) {
var user = ReportUser.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
var report = Report(id: user.reportID, display: user.reportName);
return report;
}).toList();
notifyListeners();
});
}
notifyListeners();
}
@override
void logout() {
reports = [];
if (listener != null) listener.cancel();
if (listenerUser != null) listenerUser.cancel();
}
Future<Report> getReport(String repID) async {
String path = "/$reports_collection";
var snap = await Firestore.instance.collection(path).document(repID).get();
Report report = Report.fromMap(snap.data, snap.documentID);
return report;
}
Future<List> getReportData(Report report, int limit, int offset) async {
List reportDataList = [];
var aggFun = [];
var fields = [];
var groupbys = [];
// print('report => $report');
try {
report.fields.asMap().forEach((key, value) {
if (value.aggFun == '') {
aggFun.add('');
} else {
aggFun.add(value.aggFun);
}
});
report.fields.asMap().forEach((key, value) {
fields.add(value.name);
});
String strFields;
fields.forEach((element) {
if (strFields == null) {
strFields = element;
} else {
strFields = strFields + ',' + element;
}
});
String strAgg;
aggFun.forEach((element) {
if (strAgg == null) {
strAgg = element;
} else {
strAgg = strAgg + ',' + element;
}
});
String strGroup;
groupbys.forEach((element) {
if (strGroup == null) {
strGroup = element;
} else {
strGroup = strGroup + ',' + element;
}
});
var data = {
"fields": strFields == null ? '' : strFields,
"aggfuns": strAgg == null ? '' : strAgg,
// "groupbys": strGroup == null ? '' : strGroup,
"limit": limit,
"offset": offset
};
print("payload:$data");
var rdata = {
"fields": 'quantity,product_id,product_name',
"aggfuns": ",,",
"groupbys": 'product_id',
"limit": limit,
"offset": offset
};
var result = await request("/api/data/${report.object}", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
if (result == null) return [];
result.forEach((rdata) {
reportDataList.add(rdata);
});
// print('reportDataList => $reportDataList');
notifyListeners();
return reportDataList;
} catch (e) {
log.warning("Error get Summary>>>>${e.toString()}");
}
}
Future<void> downloadReportData(Report report) async {
var aggFun = [];
var fields = [];
report.fields.asMap().forEach((key, value) {
if (value.aggFun == '') {
aggFun.add('');
} else {
aggFun.add(value.aggFun);
}
});
report.fields.asMap().forEach((key, value) {
fields.add(value.name);
});
String strFields;
fields.forEach((element) {
if (strFields == null) {
strFields = element;
} else {
strFields = strFields + ',' + element;
}
});
String strAgg;
aggFun.forEach((element) {
if (strAgg == null) {
strAgg = element;
} else {
strAgg = strAgg + ',' + element;
}
});
// final directory = await getApplicationDocumentsDirectory();
final directory = await getExternalStorageDirectory();
String path = ('${directory.path}/${report.id}.pdf');
log.info("download file path:$path");
var data = {
"fields": strFields == null ? '' : strFields,
"aggfuns": strAgg == null ? '' : strAgg,
"greoupbys": '',
"limit": 0,
"offset": 0
};
await api.requestDownloadPDFAPI("/api/report-pdf/${report.object}", "GET",
filePath: path,
url: Config.instance.reportURL,
token: await getToken(),
payload: jsonEncode(data));
final message = await OpenFile.open(path);
log.info("Open file result:$message");
}
Future<String> getJson(Report report) async {
var aggFun = [];
var fields = [];
report.fields.asMap().forEach((key, value) {
if (value.aggFun == '') {
aggFun.add('');
} else {
aggFun.add(value.aggFun);
}
});
report.fields.asMap().forEach((key, value) {
fields.add(value.name);
});
String strFields;
fields.forEach((element) {
if (strFields == null) {
strFields = element;
} else {
strFields = strFields + ',' + element;
}
});
String strAgg;
aggFun.forEach((element) {
if (strAgg == null) {
strAgg = element;
} else {
strAgg = strAgg + ',' + element;
}
});
var data = {
"fields": strFields == null ? '' : strFields,
"aggfuns": strAgg == null ? '' : strAgg,
"greoupbys": '',
"limit": 0,
"offset": 0
};
return jsonEncode(data);
}
Future<String> getEscapeJson(Report report) async {
var bytes = utf8.encode(await getJson(report));
var base64Str = base64.encode(bytes);
return HtmlEscape().convert(base64Str);
}
Future<List> getReportDataWithFilters(Report report, List filters) async {
List reportDataList = [];
var aggFun = [];
var fields = [];
var groupbys = [];
try {
var data = report.convertArrayToString(report, filters);
print('data=> $data');
var result = await request("/api/data/${report.object}", "POST",
token: await getToken(),
url: Config.instance.reportURL,
payload: jsonEncode(data));
if (result == null) return [];
result.forEach((rdata) {
reportDataList.add(rdata);
});
notifyListeners();
return reportDataList;
} catch (e) {
log.warning("Error get Summary>>>>${e.toString()}");
}
}
Future<void> addreport() async {
var data = [
{
"display": "Buyer Rpt",
"object": "buyer_rpt",
"display_filters": [
{
"name": "user_name",
"display_name": "User Name",
"compare": "==",
"data_type": "string",
},
],
"fields": [
{
"name": "user_name",
"display_name": "User Name",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "reg_date",
"display_name": "Registered Date",
"to_fixed": 0,
"type": "intdate",
"agg_fun": "",
},
{
"name": "biz_name",
"display_name": "Business Name",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "biz_address",
"display_name": "Business Address",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
],
"display_fields": ['biz_name', 'biz_name', 'reg_date', 'user_name'],
"sorts": [],
"groupbys": []
},
{
"display": "Delivery Rpt",
"object": "delivery_rpt",
"display_filters": [
{
"name": "user_name",
"display_name": "User Name",
"compare": "==",
"data_type": "string",
},
],
"fields": [
{
"name": "user_name",
"display_name": "User Name",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "delivery_ended_date",
"display_name": "Delivery Ended Date",
"to_fixed": 0,
"type": "intdate",
"agg_fun": "",
},
{
"name": "qty_92",
"display_name": "Qty 92",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_95",
"display_name": "Qty 95",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_d",
"display_name": "Qty D",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_p",
"display_name": "Qty P",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
],
"display_fields": [
'qty_p',
'qty_d',
'qty_95',
'qty_92',
'delivery_ended_date',
'user_name',
],
"sorts": [],
"groupbys": []
},
{
"display": "DOs Rpt",
"object": "dos_rpt",
"display_filters": [
{
"name": "user_name",
"display_name": "User Name",
"compare": "==",
"data_type": "string",
},
],
"fields": [
{
"name": "user_name",
"display_name": "User Name",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "doc_date",
"display_name": "DO Date",
"to_fixed": 0,
"type": "intdate",
"agg_fun": "",
},
{
"name": "doc_number",
"display_name": "PO/DO Number",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "qty_92",
"display_name": "Qty 92",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_95",
"display_name": "Qty 95",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_d",
"display_name": "Qty D",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_p",
"display_name": "Qty P",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
],
"display_fields": [
'qty_p',
'qty_d',
'qty_95',
'qty_92',
'doc_date',
'user_name',
'doc_number'
],
"sorts": [],
"groupbys": []
},
{
"display": "POs Rpt",
"object": "pos_rpt",
"display_filters": [
{
"name": "user_name",
"display_name": "User Name",
"compare": "==",
"data_type": "string",
},
{
"name": "po_number",
"display_name": "PO Number",
"compare": "==",
"data_type": "string",
}
],
"fields": [
{
"name": "user_name",
"display_name": "User Name",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "po_date",
"display_name": "PO Date",
"to_fixed": 0,
"type": "intdate",
"agg_fun": "",
},
{
"name": "po_number",
"display_name": "PO Number",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "qty_92",
"display_name": "Qty 92",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_95",
"display_name": "Qty 95",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_d",
"display_name": "Qty D",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "qty_p",
"display_name": "Qty P",
"to_fixed": 0,
"type": "float",
"agg_fun": "",
},
{
"name": "price_92",
"display_name": "Price 92",
"to_fixed": 0,
"type": "integer",
"agg_fun": "",
},
{
"name": "price_95",
"display_name": "Price 95",
"to_fixed": 0,
"type": "integer",
"agg_fun": "",
},
{
"name": "price_d",
"display_name": "Price D",
"to_fixed": 0,
"type": "integer",
"agg_fun": "",
},
{
"name": "price_p",
"display_name": "Price P",
"to_fixed": 0,
"type": "integer",
"agg_fun": "",
}
],
"display_fields": [
'qty_p',
'qty_d',
'qty_95',
'qty_92',
'price_p',
'price_d',
'price_95',
'price_92',
'po_date',
'user_name',
'po_number'
],
"sorts": [],
"groupbys": []
},
{
"display": "Storage Charge Rpt",
"object": "storage_charge_rpt",
"display_filters": [
{
"name": "user_name",
"display_name": "User Name",
"compare": "==",
"data_type": "string",
},
],
"fields": [
{
"name": "user_name",
"display_name": "User Name",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "total_qty",
"display_name": "Quantity",
"to_fixed": 3,
"type": "float",
"agg_fun": "",
},
{
"name": "rate",
"display_name": "Rate",
"to_fixed": 0,
"type": "string",
"agg_fun": "",
},
{
"name": "storage_charge",
"display_name": "Amount",
"to_fixed": 3,
"type": "integer",
"agg_fun": "",
},
],
"display_fields": [
'qty_p',
'qty_d',
'qty_95',
'qty_92',
'price_p',
'price_d',
'price_95',
'price_92',
'po_date',
'user_name',
'po_number'
],
"sorts": [],
"groupbys": []
}
];
data.asMap().forEach((key, value) {
Firestore.instance
.collection("/$reports_collection")
.document()
.setData(value);
});
}
Future<void> saveSelectedFieldsAndPosition(
String id, List<Field> positonFields, List selectedFields) async {
// positonFields.toJson
ReportFieldPositionSelection report = ReportFieldPositionSelection(
fieldPosition: positonFields, fieldSelection: selectedFields);
await SharedPref.saveReport(report);
}
Future<ReportFieldPositionSelection> loadSelectedFieldsAndPosition(
String id) async {
var data = await SharedPref.getReport(id);
return data;
}
}

View File

@@ -0,0 +1,83 @@
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/model/constants.dart';
import 'package:fcs/vo/report.dart';
import 'package:fcs/vo/report_user.dart';
import 'package:fcs/vo/user.dart';
import '../config.dart';
import 'base_model.dart';
import 'firebase_helper.dart';
class ReportUserModel extends BaseModel {
List<ReportUser> reportUsers = [];
void initUser(user) {
super.initUser(user);
}
@override
logout() async {}
Future<List<ReportUser>> getUsersForReport(String reportID) async {
List<ReportUser> users = [];
String path = "/$report_user_collection";
QuerySnapshot snapshots = await Firestore.instance
.collection(path)
.where('report_id', isEqualTo: reportID)
.getDocuments();
snapshots.documents.forEach((d) {
users.add(ReportUser.fromMap(d.data, d.documentID));
});
return users;
}
Future<List<User>> findUser(String searchUser) async {
if (searchUser == null || searchUser == '') return List();
var bytes = utf8.encode(searchUser);
var base64Str = base64.encode(bytes);
HtmlEscape htmlEscape = const HtmlEscape();
String escapeUser = htmlEscape.convert(base64Str);
int limit = 20;
List<User> _users = [];
try {
var data = await request(
"/api/fts/$user_collection/$escapeUser/$limit", "GET",
token: await getToken(), url: Config.instance.reportURL);
if (data == null) return List();
data.forEach((user) {
var _user = User.fromUserJson(user);
_users.add(_user);
});
} catch (e) {
// permission error
log.warning("user error:" + e.toString());
return null;
}
return _users;
}
Future<void> updateReportForAllUsers(Report report) async {
await request("/report", "PUT",
payload: {'id': report.id, 'for_all_users': report.forAllUser},
token: await getToken());
notifyListeners();
}
Future<void> assignUser(ReportUser reportUser) async {
await request("/report_user", "POST",
payload: reportUser.toMap(), token: await getToken());
notifyListeners();
}
Future<void> deleteReportUser(ReportUser reportUser) async {
await request("/report_user", "DELETE",
payload: reportUser.toMap(), token: await getToken());
notifyListeners();
}
}

View File

@@ -0,0 +1,76 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fcs/vo/report.dart';
import 'dart:convert';
import 'package:fcs/vo/user.dart';
class SharedPref {
static final SharedPref instance = SharedPref._();
SharedPref._();
static Future<String> getLang() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('language');
}
static Future<void> saveLang(String lang) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('language', lang);
}
static Future<void> saveUser(User user) async {
await _save("user", user.toJson());
}
static Future<void> saveReport(ReportFieldPositionSelection report) async {
await _save('report-${report.id}', report.toJson());
}
static Future<ReportFieldPositionSelection> getReport(String id) async {
try {
return ReportFieldPositionSelection.fromJson(await _read("report-$id"));
} catch (e) {
return null;
}
}
static Future<User> getUser() async {
try {
return User.fromJson(await _read("user"));
} catch (e) {
return null;
}
}
static Future<User> removeUser() async {
return await _remove("user");
}
static Future<void> saveSkippedRecoverEmail(bool skipped) async {
await _save("skipped_recovery_email", skipped);
}
static Future<bool> getSkippedRecoverEmail() async {
try {
bool _skipped = await _read("skipped_recovery_email");
return _skipped;
} catch (e) {
return null;
}
}
static _read(String key) async {
final prefs = await SharedPreferences.getInstance();
return json.decode(prefs.getString(key));
}
static _save(String key, value) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString(key, json.encode(value));
}
static _remove(String key) async {
final prefs = await SharedPreferences.getInstance();
prefs.remove(key);
}
}

View File

@@ -0,0 +1,155 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:fcs/vo/inventory_line.dart';
import 'package:fcs/vo/inventory_taking.dart';
import 'package:fcs/vo/product.dart';
import 'package:fcs/vo/storage.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class StorageModel extends BaseModel {
final log = Logger('StorageModel');
List<Storage> storages = [];
List<InventoryTaking> inventoryTakings = [];
int selectedIndex = 0;
DateTime selectedDate = DateTime.now();
List<Storage> getStorage(String productID) {
return storages
.where((s) => s.products.any((p) => p.id == productID))
.toList();
}
void initUser(user) async {
super.initUser(user);
if (!user.isOwnerAndAbove() && !user.hasInventory()) {
return;
}
_loadStorages();
_loadInventoryTakings();
}
@override
logout() async {
storages = [];
inventoryTakings = [];
}
String getStorageName(String storageID) {
return storages.firstWhere((s) => s.id == storageID).name;
}
void _loadStorages() async {
try {
getQuerySnapshotF(
"/$biz_collection/${setting.okEnergyId}/$storage_collection",
user.accountID)
.listen((QuerySnapshot snapshot) {
storages.clear();
storages = snapshot.documents.map((documentSnapshot) {
var storage = Storage.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
loadProducts(storage);
return storage;
}).toList();
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
void _loadInventoryTakings() async {
try {
String path = "/$biz_collection/${setting.okEnergyId}/$inventory_takings";
var startDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 0, 0, 0);
var endDate = new DateTime(
selectedDate.year, selectedDate.month, selectedDate.day, 23, 59, 59);
inventoryTakings.clear();
getFilterDateSnapshotF(path, user.accountID, 'date_time', startDate,
endDate, 'date_time')
.listen((QuerySnapshot snapshot) {
inventoryTakings = snapshot.documents.map((documentSnapshot) {
var data = InventoryTaking.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return data;
}).toList();
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
loadInventoryLines(InventoryTaking inventoryTaking) async {
if (inventoryTaking.linesLoaded) return;
var snaps = await getSnapshot(
"/$biz_collection/${setting.okEnergyId}/$inventory_takings/${inventoryTaking.id}/$inventory_lines");
inventoryTaking.inventoryLines =
snaps.documents.map((s) => InventoryLine.fromMap(s.data)).toList();
inventoryTaking.linesLoaded = true;
notifyListeners();
}
loadProducts(Storage storage) async {
if (storage.productsLoaded) return;
var snaps = await getSnapshot(
"/$biz_collection/${setting.okEnergyId}/$storage_collection/${storage.id}/$product_collection");
storage.products = snaps.documents
.map((s) => Product.fromMap(s.data, s.documentID))
.toList();
storage.productsLoaded = true;
notifyListeners();
}
Future<void> createStorage(Storage storage) async {
await request("/storage", "POST",
payload: storage.toMap(), token: await getToken());
}
Future<void> updateStorage(Storage storage) async {
await request("/storage", "PUT",
payload: storage.toMap(), token: await getToken());
}
Future<void> deleteStorage(String storageID) async {
await request("/storage/" + storageID, "DELETE", token: await getToken());
}
Future<void> createInventoryTaking(InventoryTaking inventoryTaking) async {
await request("/inventory", "POST",
payload: inventoryTaking.toMap(), token: await getToken());
}
void filterDate(DateTime dateTime, int _selectedIndex) async {
this.selectedIndex = _selectedIndex;
this.selectedDate = dateTime;
String path = "/$biz_collection/${setting.okEnergyId}/$inventory_takings";
var endDate =
new DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59);
inventoryTakings.clear();
getFilterDateSnapshotF(
path, user.accountID, 'date_time', dateTime, endDate, 'date_time')
.listen((snapshot) {
inventoryTakings = snapshot.documents.map((documentSnapshot) {
var data = InventoryTaking.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return data;
}).toList();
notifyListeners();
});
notifyListeners();
}
}

177
lib/model/test_model.dart Normal file
View File

@@ -0,0 +1,177 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:faker/faker.dart';
import 'package:logging/logging.dart';
import 'base_model.dart';
class TestModel extends BaseModel {
final log = Logger('TestModel');
List<Test> tests = [];
DocumentSnapshot prev;
static const int row_count = 10;
bool ended = false;
StreamSubscription<QuerySnapshot> listener;
final Query queryBase = Firestore.instance.collection("/tests");
final Query query = Firestore.instance
.collection("/tests")
// .orderBy("deleted")
.orderBy("age", descending: false);
void initData() async {
_clearState();
_initListener();
load();
}
void _clearState() {
prev = null;
tests = [];
ended = false;
if (listener != null) listener.cancel();
listener = null;
}
void _initListener() {
Query _query = queryBase.orderBy("update_time", descending: true).limit(1);
_query.getDocuments(source: Source.server).then((QuerySnapshot snapshot) {
int count = snapshot.documents.length;
if (count == 1) {
var test = Test.fromMap(
snapshot.documents[0].data, snapshot.documents[0].documentID);
Query _queryListener = queryBase
.where("update_time", isGreaterThan: test.updateTime)
.orderBy("update_time", descending: true);
listener =
_queryListener.snapshots(includeMetadataChanges: true).listen((qs) {
qs.documentChanges.forEach((c) {
switch (c.type) {
case DocumentChangeType.added:
var test = Test.fromMap(c.document.data, c.document.documentID);
if (tests.contains(test)) {
tests[tests.indexOf(test)].name = test.name;
notifyListeners();
}
if (!tests.contains(test)) {
tests.add(test);
notifyListeners();
}
break;
case DocumentChangeType.modified:
var test = Test.fromMap(c.document.data, c.document.documentID);
if (tests.contains(test)) {
bool deleted = c.document.data["deleted"];
if (deleted != null && deleted) {
tests.remove(test);
} else {
tests[tests.indexOf(test)].name = test.name;
}
notifyListeners();
}
break;
default:
}
});
});
notifyListeners();
}
});
}
Future<void> load() async {
Query _query = prev != null ? query.startAfterDocument(prev) : query;
try {
_query
// .where("deleted", isNull: null)
.limit(row_count)
.getDocuments(source: Source.server)
.then((QuerySnapshot snapshot) {
int count = snapshot.documents.length;
ended = count < row_count;
prev = count > 0 ? snapshot.documents[count - 1] : prev;
snapshot.documents.forEach((e) {
var test = Test.fromMap(e.data, e.documentID);
if (!tests.contains(test)) tests.add(test);
});
notifyListeners();
});
} catch (e) {
log.warning("Error!! $e");
}
}
void populate() {
for (var i = 0; i < 30; i++) {
Firestore.instance
.collection('tests')
.document(faker.person.name())
.setData({
'name': faker.person.name(),
'age': random.decimal(),
'update_time': DateTime.now().microsecondsSinceEpoch
});
}
}
void add() {
Firestore.instance
.collection('tests')
.document(faker.person.name())
.setData({
'name': faker.person.name(),
'age': random.decimal(),
'update_time': DateTime.now().microsecondsSinceEpoch
});
}
void update() {
Firestore.instance.collection('tests').document(tests[0].id).setData({
'name': faker.person.name(),
'update_time': DateTime.now().microsecondsSinceEpoch
}, merge: true);
}
void remove() {
Firestore.instance.collection('tests').document(tests[0].id).setData(
{'deleted': 1, 'update_time': DateTime.now().microsecondsSinceEpoch},
merge: true);
}
@override
void logout() {
_clearState();
}
}
class Test {
String id;
String name;
double age;
int updateTime;
Test(this.id, {this.name, this.age, this.updateTime});
factory Test.fromMap(Map<String, dynamic> map, String id) {
return Test(id,
name: map['name'], age: map['age'], updateTime: map['update_time']);
}
@override
bool operator ==(other) {
if (identical(this, other)) {
return true;
}
return other.id == this.id;
}
@override
int get hashCode {
int result = 17;
result = 37 * result + id.hashCode;
return result;
}
}

368
lib/model/user_model.dart Normal file
View File

@@ -0,0 +1,368 @@
import 'dart:async';
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import 'package:fcs/util.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/vo/role.dart';
import '../config.dart';
import '../vo/user.dart';
import 'base_model.dart';
import 'constants.dart';
import 'firebase_helper.dart';
class UserModel extends BaseModel {
final log = Logger('UserModel');
StreamSubscription<QuerySnapshot> listener;
PopupMenu popupMenu = new PopupMenu();
List<User> users = [];
List<Privilege> privileges = [];
List<UserLevel> userLevels = [];
User user = new User();
List<Privilege> get getPrivileges {
return privileges
.where((p) => !p.sysAdminOnly || user.isSysAdmin())
.toList();
}
List<dynamic> cont = [];
void initUser(user) async {
super.initUser(user);
this.user = user;
if (user.isBuyer()) return;
_loadUsers(user);
_loadPrivileges();
_loadUserLevels(user);
}
@override
logout() async {
users = [];
userLevels = [];
}
List<Privilege> getUserPrivileges() {
List<Privilege> result = new List();
if (user.privilegeIds.isNotEmpty) {
user.privilegeIds.forEach((pID) {
privileges.forEach((p) {
if (p.id == pID) {
var _priv = Privilege(id: p.id, name: p.name, desc: p.desc);
result.add(_priv);
}
});
});
}
return result;
}
List<User> getBlockListUsers() {
return users.where((u) => u.isBlock == true).toList();
}
List<User> getUserList() {
return users.where((u) => u.docID != this.user.docID).toList();
}
Future<void> _loadUsers(User user) async {
try {
String path = "/$biz_collection/${setting.okEnergyId}/$user_collection";
var snaps = await Firestore.instance
.collection(path)
.where('user_level', isLessThanOrEqualTo: user.userLevel)
.limit(1)
.getDocuments();
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection(path)
.where('user_level', isLessThanOrEqualTo: user.userLevel)
.snapshots();
snapshots.listen((snaps) async {
users = snaps.documents.map((documentSnapshot) {
var data =
User.fromMap(documentSnapshot.data, documentSnapshot.documentID);
if (data.docID == user.docID && data.isBlock) {
this.mainModel.logout();
notifyListeners();
}
return data;
}).toList();
notifyListeners();
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<User> findUser(String phoneNumber) async {
var _phoneNumber = updatePhoneNumber(phoneNumber);
try {
var data = await request("/user/find/$_phoneNumber", "GET",
token: await getToken());
return User.fromJson(data);
} catch (e) {
throw Exception(e);
}
}
Future<void> _loadPrivileges() async {
// if (!user.isOwner() && !user.hasAccount()) {
// return;
// }
try {
getQuerySnapshot("/$privilege_collection")
.listen((QuerySnapshot snapshot) {
privileges.clear();
privileges = snapshot.documents.map((documentSnapshot) {
var privilege = Privilege.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return privilege;
}).toList();
notifyListeners();
}).onError((e) {
log.warning("Error! $e");
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<void> _loadUserLevels(User user) async {
try {
Stream<QuerySnapshot> snapshots = Firestore.instance
.collection("/$user_level_collection")
.where('level', isLessThan: user.userLevel)
.snapshots();
snapshots.listen((snaps) async {
userLevels = snaps.documents
.map((documentSnapshot) => UserLevel.fromMap(
documentSnapshot.data, documentSnapshot.documentID))
.toList();
notifyListeners();
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<User> getUser(String id) async {
String path = "/$biz_collection/${setting.okEnergyId}/$user_collection";
print("id=> $id");
var snap = await getDocSnap(path, id);
print("snap=> $snap");
return User.fromMap(snap.data, snap.documentID);
}
Future<void> addUserToOk(String userID, List<String> privileges) async {
await addUserToBiz(userID, setting.okEnergyId, privileges);
}
Future<void> addUserToBiz(
String userID, bizID, List<String> privileges) async {
try {
await request("/user/add-biz", "POST",
payload: {
"user_id": userID,
"biz_id": bizID,
"privileges": privileges
},
token: await getToken());
} catch (e) {
throw Exception(e);
}
}
Future<void> forgetPassword(String id) async {
var _id = updatePhoneNumber(id);
await request("/forget", "POST", payload: {"id": _id});
}
Future<void> resetPassword(
String id, String newPassword, String confirmationCode) async {
var _id = updatePhoneNumber(id);
await request(
"/reset",
"POST",
payload: {
"id": _id,
"password": newPassword,
"confirmation_code": confirmationCode
},
);
}
Future<void> changePassword(String id, String newPassword) async {
await request(
"/change/password",
"POST",
payload: {
"id": id,
"password": newPassword,
},
);
}
Future<void> changePhone(String id, String newPhone) async {
var _newPhone = updatePhoneNumber(newPhone);
await request(
"/change/phone",
"POST",
payload: {
"id": id,
"phone_number": _newPhone,
},
);
}
Future<void> addEmail(String id, String email) async {
await request(
"/email",
"PUT",
payload: {
"id": id,
"email": email,
},
);
}
Future<void> confirmEmail(
String id, String email, String phone, String confirmCode) async {
var _id = updatePhoneNumber(id);
var _phone = updatePhoneNumber(phone);
await request(
"/econfirm",
"POST",
payload: {
"id": _id,
"email": email == null ? '' : email,
"phone_number": _phone == null ? '' : _phone,
"confirmation_code": confirmCode
},
);
}
Future<void> deleteStorage(String storageID) async {
await request("/storage/" + storageID, "DELETE", token: await getToken());
}
Future<void> blockPhone(String phone) async {
var _phone = updatePhoneNumber(phone);
await request("/blist", "PUT",
payload: {"phone_number": _phone}, token: await getToken());
}
Future<void> unblockPhone(String phone) async {
var _phone = updatePhoneNumber(phone);
await request("/wlist", "PUT",
payload: {"phone_number": _phone}, token: await getToken());
}
Future<void> addLevel(
String phone, String levelId, List<String> privs) async {
var _phone = updatePhoneNumber(phone);
await request("/lvl", "PUT",
payload: {
"phone_number": _phone,
"user_level_id": levelId,
"privileges": privs
},
token: await getToken());
}
Future<void> updatePin(String pin, String password) async {
await request("/pin", "POST",
payload: {
"id": user.docID,
"pin": pin,
"password": password,
},
token: await getToken());
}
Future<void> clearPin(String password) async {
await request("/pin/clear", "POST",
payload: {
"id": user.docID,
"password": password,
},
token: await getToken());
}
Future<List<User>> searchUser(String searchUser) async {
if (searchUser == null || searchUser == '') return List();
var bytes = utf8.encode(searchUser);
var base64Str = base64.encode(bytes);
HtmlEscape htmlEscape = const HtmlEscape();
String escapeUser = htmlEscape.convert(base64Str);
int limit = 20;
List<User> _users = [];
try {
var data = await request(
"/api/fts/$user_collection/$escapeUser/$limit", "GET",
token: await getToken(), url: Config.instance.reportURL);
if (data == null) return List();
data.forEach((user) {
var _user = User.fromUserJson(user);
_users.add(_user);
});
} catch (e) {
// permission error
log.warning("user error:" + e.toString());
return null;
}
return _users;
}
void filterSorting(int _selectedIndex) {
users.clear();
if (listener != null) {
listener.cancel();
}
String _fieldName;
bool descending = false;
if (_selectedIndex == 0) {
_fieldName = 'user_name';
descending = false;
}
if (_selectedIndex == 1) {
_fieldName = 'user_name';
descending = true;
}
if (_selectedIndex == 2) {
_fieldName = 'phone_number';
descending = false;
}
if (_selectedIndex == 3) {
_fieldName = 'phone_number';
descending = true;
}
this.popupMenu.index = _selectedIndex;
String path = "/$biz_collection/${setting.okEnergyId}/$user_collection";
listener =
getFilterSnapshot(path, descending, _fieldName).listen((snaps) async {
users.clear();
snaps.documents.forEach((d) {
users.add(User.fromMap(d.data, d.documentID));
notifyListeners();
});
users.where((user) => user.userLevel <= this.user.userLevel);
notifyListeners();
});
}
}