595 lines
14 KiB
Dart
595 lines
14 KiB
Dart
import 'dart:convert';
|
|
import 'package:cdb_ui/pages/supply.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
// todo : api errors
|
|
|
|
class API {
|
|
SharedPreferences? pref;
|
|
static final API _instance = API._internal();
|
|
|
|
// cache
|
|
List<Item>? items;
|
|
Map<String, Location>? locations;
|
|
Map<String, FlowInfo>? flowInfos;
|
|
|
|
factory API() {
|
|
return _instance;
|
|
}
|
|
|
|
API._internal();
|
|
|
|
Future<void> prefetch() async {
|
|
// todo : prefetch
|
|
// fetch items
|
|
var resp = jsonDecode(await getRequest("$instance/items"));
|
|
var lst = resp["items"] as List<dynamic>;
|
|
items = lst.map((x) => Item(x)).toList();
|
|
|
|
// fetch locations
|
|
var locResp = jsonDecode(await getRequest("$instance/locations"))
|
|
as Map<String, dynamic>;
|
|
locations = locResp.map((key, value) => MapEntry(key, Location(value)));
|
|
|
|
// fetch flowInfos
|
|
var flowResp =
|
|
jsonDecode(await getRequest("$instance/flows")) as Map<String, dynamic>;
|
|
|
|
flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value)));
|
|
}
|
|
|
|
Future<void> init(Function refresh) async {
|
|
pref = await SharedPreferences.getInstance();
|
|
instance = pref!.getString("instance") ?? "";
|
|
refresh();
|
|
}
|
|
|
|
bool isInit() {
|
|
if (pref == null) {
|
|
return false;
|
|
}
|
|
|
|
return pref!.containsKey("token") && pref!.containsKey("instance");
|
|
}
|
|
|
|
bool isPrefetched() {
|
|
return items != null && locations != null && flowInfos != null;
|
|
}
|
|
|
|
void save(String instance, String token) {
|
|
pref!.setString("instance", instance);
|
|
pref!.setString("token", token);
|
|
this.instance = instance;
|
|
}
|
|
|
|
String instance = "";
|
|
|
|
Future<String> getRequest(String url) async {
|
|
var resp = await http.get(Uri.parse(url), headers: <String, String>{
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'application/json; charset=UTF-8',
|
|
'Token': pref!.getString("token")!
|
|
});
|
|
|
|
return utf8.decode(resp.bodyBytes);
|
|
}
|
|
|
|
Future<String> postRequest(String url, Map<String, dynamic> data) async {
|
|
var resp = await http.post(Uri.parse(url),
|
|
headers: <String, String>{
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'application/json; charset=UTF-8',
|
|
'Token': pref!.getString("token")!
|
|
},
|
|
body: jsonEncode(data));
|
|
|
|
return utf8.decode(resp.bodyBytes);
|
|
}
|
|
|
|
// /items
|
|
List<Item> getItems() {
|
|
return items!;
|
|
}
|
|
|
|
Future<GlobalItemStat> getGlobalItemStat() async {
|
|
var resp = jsonDecode(await getRequest("$instance/items/stat"));
|
|
return GlobalItemStat(resp);
|
|
}
|
|
|
|
// /item/<item>/<variant>/unique?<field>
|
|
Future<List<String>> getUniqueField(
|
|
String item, String variant, String field) async {
|
|
var resp = jsonDecode(
|
|
await getRequest("$instance/item/$item/$variant/unique?field=$field"));
|
|
List<String> ret = [];
|
|
|
|
for (var e in resp as List<dynamic>) {
|
|
ret.add(e);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Map<String, Location> getLocations() {
|
|
return locations!;
|
|
}
|
|
|
|
Item getItem(String item) {
|
|
return items!.firstWhere((x) => x.id == item);
|
|
}
|
|
|
|
Future<Transaction> getTransaction(String id) async {
|
|
return Transaction(
|
|
jsonDecode(await getRequest("$instance/transaction/$id")));
|
|
}
|
|
|
|
Future<List<Transaction>> getTransactionsOfLocation(String location,
|
|
{bool recursive = true}) async {
|
|
var url = "$instance/location/$location/inventory";
|
|
|
|
if (recursive) {
|
|
url += "?recursive=true";
|
|
}
|
|
|
|
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
|
|
|
|
return resp.map((x) => Transaction(x)).toList();
|
|
}
|
|
|
|
Future<List<Transaction>> getConsumedItems(String item, String variant,
|
|
{String? destination}) async {
|
|
var url = "$instance/item/$item/$variant/demand";
|
|
if (destination != null) {
|
|
url += "?destination=$destination";
|
|
}
|
|
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
|
|
|
|
return resp.map((x) => Transaction(x)).toList();
|
|
}
|
|
|
|
// /item/<item_id>/inventory?<origin>
|
|
Future<List<Transaction>> getInventory(String item, {String? origin}) async {
|
|
var url = "$instance/item/$item/inventory";
|
|
|
|
if (origin != null) {
|
|
url += "?origin=$origin";
|
|
}
|
|
|
|
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
|
|
|
|
return resp.map((x) => Transaction(x)).toList();
|
|
}
|
|
|
|
// /item/<item_id>/<variant_id>/inventory
|
|
Future<List<Transaction>> getInventoryOfVariant(
|
|
String item, String variant) async {
|
|
var resp =
|
|
jsonDecode(await getRequest("$instance/item/$item/$variant/inventory"))
|
|
as List<dynamic>;
|
|
|
|
return resp.map((x) => Transaction(x)).toList();
|
|
}
|
|
|
|
// /supply
|
|
Future<String> supplyItem(String item, String variant, double price,
|
|
String? origin, String? location, String? note) async {
|
|
if (origin!.isEmpty) {
|
|
origin = null;
|
|
}
|
|
|
|
if (location!.isEmpty) {
|
|
location = null;
|
|
}
|
|
|
|
if (note!.isEmpty) {
|
|
note = null;
|
|
}
|
|
|
|
var req = await postRequest("$instance/supply", {
|
|
"item": item,
|
|
"variant": variant,
|
|
"price": price,
|
|
"origin": origin,
|
|
"location": location,
|
|
"note": note
|
|
});
|
|
var resp = jsonDecode(req);
|
|
|
|
return resp["uuid"];
|
|
}
|
|
|
|
// /demand
|
|
Future<void> consumeItem(
|
|
String transaction, String destination, double price) async {
|
|
await postRequest("$instance/demand",
|
|
{"uuid": transaction, "destination": destination, "price": price});
|
|
}
|
|
|
|
// /item/<item_id>/<variant_id>/stat
|
|
Future<dynamic> getStat(String item, String variant,
|
|
{bool full = false}) async {
|
|
if (full) {
|
|
return FullItemVariantStat(jsonDecode(
|
|
await getRequest("$instance/item/$item/$variant/stat?full=true")));
|
|
}
|
|
|
|
return ItemVariantStat(
|
|
jsonDecode(await getRequest("$instance/item/$item/$variant/stat")));
|
|
}
|
|
|
|
String getImageURL(String item) {
|
|
return "$instance/$item/image";
|
|
}
|
|
|
|
// /item/<item_id>/<variant_id>/price_history?<origin>
|
|
Future<List<double>> getPriceHistory(String item, String variant,
|
|
{String? origin}) async {
|
|
var url = "$instance/item/$item/$variant/price_history";
|
|
|
|
if (origin != null) {
|
|
url += "?origin=$origin";
|
|
}
|
|
|
|
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
|
|
|
|
return resp.map((x) => x as double).toList();
|
|
}
|
|
|
|
Future<double> getLatestPrice(String item, String variant,
|
|
{String? origin}) async {
|
|
var url = "$instance/item/$item/$variant/price_latest";
|
|
|
|
if (origin != null) {
|
|
url += "?origin=$origin";
|
|
}
|
|
|
|
var resp = jsonDecode(await getRequest(url)) as Map<String, dynamic>;
|
|
|
|
return resp as double;
|
|
}
|
|
|
|
// Flows
|
|
|
|
// /flows
|
|
Map<String, FlowInfo> getFlows() {
|
|
return flowInfos!;
|
|
}
|
|
|
|
// /flow/<id>/info
|
|
FlowInfo getFlowInfo(String id) {
|
|
return flowInfos![id]!;
|
|
}
|
|
|
|
Future<Flow> getFlow(String id) async {
|
|
return Flow(jsonDecode(await getRequest("$instance/flow/$id")));
|
|
}
|
|
|
|
Future<List<Flow>> getActiveFlowsOf(String id) async {
|
|
var res = jsonDecode(await getRequest("$instance/flow/$id/active"))
|
|
as List<dynamic>;
|
|
|
|
return res.map((x) => Flow(x)).toList();
|
|
}
|
|
|
|
// /flow/<id>
|
|
Future<String> startFlow(String id, {List<String>? input}) async {
|
|
var resp =
|
|
jsonDecode(await postRequest("$instance/flow/$id", {"input": input}));
|
|
|
|
return resp["uuid"];
|
|
}
|
|
|
|
// /flow/<id>/end
|
|
Future<Map<String, List<String>>?> endFlow(String id,
|
|
{List<SupplyForm>? produced}) async {
|
|
var resp = jsonDecode(await postRequest("$instance/flow/$id/end",
|
|
{"produced": produced?.map((x) => x.json()).toList()}));
|
|
|
|
if (produced != null) {
|
|
var produced = resp["produced"] as Map<String, dynamic>;
|
|
return produced.map(
|
|
(key, value) {
|
|
return MapEntry(key, (value as List<dynamic>).cast<String>());
|
|
},
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// /flow/<id>/continue
|
|
Future<String> continueFlow(String id, {List<String>? input}) async {
|
|
var resp = jsonDecode(
|
|
await postRequest("$instance/flow/$id/continue", {"input": input}));
|
|
|
|
return resp["uuid"];
|
|
}
|
|
|
|
Future<List<Transaction>> getExpiredItems() async {
|
|
var resp = jsonDecode(await getRequest("$instance/items/expired"))
|
|
as List<dynamic>;
|
|
return resp.map((x) => Transaction(x)).toList();
|
|
}
|
|
|
|
Future<List<MinItem>> getItemsUnderMin() async {
|
|
var resp =
|
|
jsonDecode(await getRequest("$instance/items/min")) as List<dynamic>;
|
|
|
|
return resp.map((x) => MinItem(x)).toList();
|
|
}
|
|
|
|
Future<void> moveTransaction(String id, String newLocation) async {
|
|
jsonDecode(await postRequest(
|
|
"$instance/transaction/$id/move", {"to": newLocation}));
|
|
}
|
|
|
|
Location getLocation(String id) {
|
|
return locations![id]!;
|
|
}
|
|
|
|
Future<String> addNoteToFlow(String flowID, String content) async {
|
|
var res = jsonDecode(
|
|
await postRequest("$instance/flow/$flowID/note", {"content": content}));
|
|
return res["uuid"];
|
|
}
|
|
|
|
Future<List<FlowNote>> getNotesOfFlow(String flowID) async {
|
|
var resp = jsonDecode(await getRequest("$instance/flow/$flowID/notes"))
|
|
as List<dynamic>;
|
|
return resp.map((x) => FlowNote(x)).toList();
|
|
}
|
|
}
|
|
|
|
class FlowInfo {
|
|
late String id;
|
|
late String name;
|
|
late List<String> depends;
|
|
late String? next;
|
|
late List<String>? produces;
|
|
|
|
FlowInfo(Map<String, dynamic> json) {
|
|
id = json["id"];
|
|
name = json["name"];
|
|
depends = (json["depends"] as List<dynamic>).cast<String>();
|
|
next = json["next"];
|
|
produces = json["produces"] != null
|
|
? (json["produces"] as List<dynamic>).cast<String>()
|
|
: null;
|
|
}
|
|
}
|
|
|
|
class Item {
|
|
late String id;
|
|
late String? image;
|
|
late String name;
|
|
late String? category;
|
|
late Map<String, ItemVariant> variants;
|
|
|
|
Item(Map<String, dynamic> json) {
|
|
id = json["uuid"];
|
|
image = json["image"];
|
|
name = json["name"];
|
|
category = json["category"];
|
|
variants = <String, ItemVariant>{};
|
|
|
|
json["variants"].forEach((key, value) {
|
|
variants[key] = ItemVariant(value);
|
|
});
|
|
}
|
|
}
|
|
|
|
class ItemVariant {
|
|
late String item;
|
|
late String variant;
|
|
late String name;
|
|
int? min;
|
|
int? expiry;
|
|
List<int>? barcodes;
|
|
|
|
ItemVariant(Map<String, dynamic> json) {
|
|
item = json["item"];
|
|
variant = json["variant"];
|
|
name = json["name"];
|
|
min = json["min"];
|
|
expiry = json["expiry"];
|
|
barcodes = json["barcodes"] != null
|
|
? (json["barcodes"] as List<dynamic>).cast<int>()
|
|
: null;
|
|
}
|
|
}
|
|
|
|
class Transaction {
|
|
late String uuid;
|
|
late String item;
|
|
late String variant;
|
|
late double price;
|
|
String? origin;
|
|
late int timestamp;
|
|
ConsumeInfo? consumed;
|
|
late bool expired;
|
|
String? note;
|
|
Location? location;
|
|
|
|
Transaction(Map<String, dynamic> json) {
|
|
uuid = json["uuid"];
|
|
item = json["item"];
|
|
variant = json["variant"];
|
|
price = json["price"];
|
|
origin = json["origin"];
|
|
timestamp = json["timestamp"];
|
|
expired = json["expired"];
|
|
note = json["note"];
|
|
consumed = json["consumed"] != null ? ConsumeInfo(json["consumed"]) : null;
|
|
location = json["location"] != null ? Location(json["location"]) : null;
|
|
}
|
|
|
|
Transaction.inMemory(String itemID, String variantID, this.price,
|
|
String? origin, Location? location, String? note) {
|
|
uuid = "";
|
|
item = itemID;
|
|
variant = variantID;
|
|
origin = origin;
|
|
timestamp = 0;
|
|
consumed = null;
|
|
expired = false;
|
|
note = note;
|
|
location = location;
|
|
}
|
|
}
|
|
|
|
class ConsumeInfo {
|
|
late String destination;
|
|
late double price;
|
|
late int timestamp;
|
|
|
|
ConsumeInfo(Map<String, dynamic> json) {
|
|
destination = json["destination"];
|
|
price = json["price"];
|
|
timestamp = json["timestamp"];
|
|
}
|
|
}
|
|
|
|
class ItemVariantStat {
|
|
late int amount;
|
|
late double totalPrice;
|
|
|
|
ItemVariantStat(Map<String, dynamic> json) {
|
|
amount = json["amount"];
|
|
totalPrice = json["total_price"];
|
|
}
|
|
}
|
|
|
|
class Location {
|
|
late String id;
|
|
late String name;
|
|
late String? parent;
|
|
late LocationCondition? conditions;
|
|
|
|
Location.zero() {
|
|
id = "";
|
|
name = "";
|
|
parent = null;
|
|
conditions = null;
|
|
}
|
|
|
|
Location(Map<String, dynamic> json) {
|
|
id = json["id"];
|
|
name = json["name"];
|
|
parent = json["parent"];
|
|
conditions = json["conditions"] != null ? (json["conditions"]) : null;
|
|
}
|
|
|
|
String fullNamePath(Map<String, Location> locations) {
|
|
var name = this.name;
|
|
|
|
if (parent != null) {
|
|
name = "${locations[parent!]!.fullNamePath(locations)} / $name";
|
|
}
|
|
|
|
return name;
|
|
}
|
|
}
|
|
|
|
class LocationCondition {
|
|
late String temperature;
|
|
|
|
LocationCondition(Map<String, dynamic> json) {
|
|
temperature = json["temperature"];
|
|
}
|
|
}
|
|
|
|
class MinItem {
|
|
late String itemVariant;
|
|
late int need;
|
|
MinItem(Map<String, dynamic> json) {
|
|
itemVariant = json["item_variant"];
|
|
need = json["need"];
|
|
}
|
|
}
|
|
|
|
class Flow {
|
|
late String id;
|
|
late int started;
|
|
late String kind;
|
|
late List<String>? input;
|
|
late FlowDone? done;
|
|
|
|
Flow(Map<String, dynamic> json) {
|
|
id = json["id"];
|
|
started = json["started"];
|
|
kind = json["kind"];
|
|
input = json["input"] != null
|
|
? (json["input"] as List<dynamic>).cast<String>()
|
|
: null;
|
|
done = (json["done"] != null) ? FlowDone(json["done"]) : null;
|
|
}
|
|
}
|
|
|
|
class FlowDone {
|
|
late int ended;
|
|
late String? next;
|
|
late List<String>? produced;
|
|
|
|
FlowDone(Map<String, dynamic> json) {
|
|
ended = json["ended"];
|
|
next = json["next"];
|
|
produced = json["produced"];
|
|
}
|
|
}
|
|
|
|
class FlowNote {
|
|
late String uuid;
|
|
late int timestamp;
|
|
late String content;
|
|
late String onFlow;
|
|
|
|
FlowNote(Map<String, dynamic> json) {
|
|
uuid = json["uuid"];
|
|
timestamp = json["timestamp"];
|
|
content = json["content"];
|
|
onFlow = json["on_flow"];
|
|
}
|
|
}
|
|
|
|
class GlobalItemStat {
|
|
late int itemCount;
|
|
late int totalTransactions;
|
|
late double totalPrice;
|
|
|
|
GlobalItemStat(Map<String, dynamic> json) {
|
|
itemCount = json["item_count"];
|
|
totalTransactions = json["total_transactions"];
|
|
totalPrice = json["total_price"];
|
|
}
|
|
}
|
|
|
|
(String, String) itemVariant(String iv) {
|
|
var split = iv.split("::");
|
|
return (split[0], split[1]);
|
|
}
|
|
|
|
class FullItemVariantStat {
|
|
late int amount;
|
|
late double totalPrice;
|
|
late double expiryRate;
|
|
late Map<String, OriginStat> origins;
|
|
|
|
FullItemVariantStat(Map<String, dynamic> json) {
|
|
amount = json["amount"];
|
|
totalPrice = json["total_price"];
|
|
expiryRate = json["expiry_rate"];
|
|
origins = (json["origins"] as Map<String, dynamic>)
|
|
.map((key, value) => MapEntry(key, OriginStat(value)));
|
|
}
|
|
}
|
|
|
|
class OriginStat {
|
|
late double averagePrice;
|
|
late int inventory;
|
|
|
|
OriginStat(Map<String, dynamic> json) {
|
|
averagePrice = json["average_price"];
|
|
inventory = json["inventory"];
|
|
}
|
|
}
|