From 70b798f65f99fc843d2b4a552f907dda6d88b54a Mon Sep 17 00:00:00 2001 From: JMARyA Date: Wed, 25 Sep 2024 18:14:42 +0200 Subject: [PATCH] add produce flows --- lib/api.dart | 25 ++++- lib/pages/flow/active_flow_page.dart | 3 +- lib/pages/flow/end_flow_page.dart | 52 +++++++++- lib/pages/flow/flow_note.dart | 4 +- lib/pages/supply.dart | 147 ++++++++++++++++++++------- lib/pages/transaction.dart | 9 +- 6 files changed, 192 insertions(+), 48 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index b10e80e..af93ada 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; @@ -249,7 +250,9 @@ class API { } // /flow//end - Future?> endFlow(String id, {Map? produced}) async { + Future?> endFlow(String id, {List? produced}) async { +// todo : update api for supplyform + var resp = jsonDecode( await postRequest("$instance/$id/end", {"produced": produced})); @@ -365,6 +368,12 @@ class Price { currency = json["currency"]; } + Price.fromString(String value) { + var priceSplit = value.split(" "); + this.value = double.parse(priceSplit[0]); + currency = priceSplit[1]; + } + String format() { return "${value.toStringAsFixed(2)} $currency"; } @@ -394,6 +403,20 @@ class Transaction { consumed = json["consumed"] != null ? ConsumeInfo(json["consumed"]) : null; location = json["location"] != null ? Location(json["location"]) : null; } + + Transaction.inMemory(String itemID, String variant, String price, + String? origin, Location? location, String? note) { + uuid = ""; + item = itemID; + variant = variant; + this.price = Price.fromString(price); + origin = origin; + timestamp = 0; + consumed = null; + expired = false; + note = note; + location = location; + } } class ConsumeInfo { diff --git a/lib/pages/flow/active_flow_page.dart b/lib/pages/flow/active_flow_page.dart index 240cb03..c0aef7f 100644 --- a/lib/pages/flow/active_flow_page.dart +++ b/lib/pages/flow/active_flow_page.dart @@ -85,7 +85,8 @@ class _ActiveFlowPageState extends State { children: [ Text("ID: ${widget.flow.id}"), Text("Started since: ${tsFormat(widget.flow.started)}"), - ...widget.flow.input!.map((x) => Text("Input: $x")).toList(), + if (widget.flow.input != null) + ...widget.flow.input!.map((x) => Text("Input: $x")).toList(), if (widget.flow.done != null) ...[ Text("Ended: ${tsFormat(widget.flow.done!.ended)}"), if (widget.flow.done!.next != null) diff --git a/lib/pages/flow/end_flow_page.dart b/lib/pages/flow/end_flow_page.dart index 4525ea8..66c70ef 100644 --- a/lib/pages/flow/end_flow_page.dart +++ b/lib/pages/flow/end_flow_page.dart @@ -1,5 +1,8 @@ +import 'dart:js_interop_unsafe'; + import 'package:cdb_ui/api.dart' as API; import 'package:cdb_ui/api.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; @@ -14,7 +17,30 @@ class EndFlowWithProduce extends StatefulWidget { } class _EndFlowWithProduceState extends State { - Map produces = {}; + List produces = []; + late Map locations; + + @override + void initState() { + super.initState(); + API.API().getLocations().then( + (value) { + setState(() { + locations = value; + }); + }, + ); + } + + refresh() { + setState(() {}); + } + + void addProduced(SupplyForm t) { + setState(() { + produces.add(t); + }); + } List addProduceButtons() { List ret = []; @@ -22,7 +48,21 @@ class _EndFlowWithProduceState extends State { for (var i in widget.info.produces!) { ret.add(ElevatedButton( onPressed: () { - // todo : implement adding + var (itemID, variant) = API.itemVariant(i); + API.API().getItem(itemID).then((item) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return SupplyPage( + item, + refresh, + onlyVariants: [variant], + forcePrice: "0.00", + forceOrigin: "flow::${widget.flow.kind}::${widget.flow.id}", + onCreate: addProduced, + ); + }, + )); + }); }, child: Text("Produced $i"))); } @@ -48,6 +88,14 @@ class _EndFlowWithProduceState extends State { ...addProduceButtons(), const Divider(), // todo : add produced list + ...produces.map((x) { + return TransactionCard( + x.transaction(locations), + () {}, + onLongPress: (x) {}, + onTap: (x) {}, + ); + }).toList(), const SizedBox( height: 10, ), diff --git a/lib/pages/flow/flow_note.dart b/lib/pages/flow/flow_note.dart index 5e8b2eb..11afbfb 100644 --- a/lib/pages/flow/flow_note.dart +++ b/lib/pages/flow/flow_note.dart @@ -9,7 +9,7 @@ class AddNotePage extends StatelessWidget { AddNotePage(this.flow, this.refresh, {super.key}); - _submit(BuildContext context) { + void _submit(BuildContext context) { API.API().addNoteToFlow(flow.id, _noteController.text).then((x) { refresh(); Navigator.of(context).pop(); @@ -32,7 +32,7 @@ class AddNotePage extends StatelessWidget { height: 14, ), ElevatedButton( - onPressed: _submit(context), child: const Text("Add Note")) + onPressed: () => _submit(context), child: const Text("Add Note")) ], ), ); diff --git a/lib/pages/supply.dart b/lib/pages/supply.dart index b83812b..1b214e8 100644 --- a/lib/pages/supply.dart +++ b/lib/pages/supply.dart @@ -5,14 +5,26 @@ import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class SupplyPage extends StatefulWidget { final Item item; final Function refresh; + final List? onlyVariants; + final String? forcePrice; + final String? forceOrigin; - const SupplyPage(this.item, this.refresh, {super.key}); + // callback function for receiving a transaction without creating on the API + final Function(SupplyForm)? onCreate; + + const SupplyPage(this.item, this.refresh, + {this.onlyVariants, + this.onCreate, + this.forceOrigin, + this.forcePrice, + super.key}); @override State createState() => _SupplyPageState(); } class _SupplyPageState extends State { + late List availableVariants; late String variant; final _formKey = GlobalKey(); String _selectedOrigin = ""; @@ -23,8 +35,13 @@ class _SupplyPageState extends State { @override void initState() { super.initState(); + availableVariants = + widget.onlyVariants ?? widget.item.variants.keys.toList(); + variant = widget.item.variants.keys.first; - _priceController = TextEditingController(text: ""); + + _selectedOrigin = widget.forceOrigin ?? ""; + _priceController = TextEditingController(text: widget.forcePrice ?? ""); _noteController = TextEditingController(text: ""); } @@ -32,6 +49,23 @@ class _SupplyPageState extends State { if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); + if (widget.onCreate != null) { + // todo : create shadow transaction + var t = SupplyForm( + itemID: widget.item.id, + variant: variant, + price: "${_priceController.text} €", + origin: _selectedOrigin, + location: _selectedLocation, + note: _noteController.text); + + widget.onCreate!(t); + + Navigator.of(context).pop(); + widget.refresh(); + return; + } + API() .supplyItem(widget.item.id, variant, "${_priceController.text} €", _selectedOrigin, _selectedLocation, _noteController.text) @@ -78,10 +112,11 @@ class _SupplyPageState extends State { variant = value!; }); }, - items: widget.item.variants.entries.map((entry) { + items: availableVariants.map((entryKey) { + var entry = widget.item.variants[entryKey]!; return DropdownMenuItem( - value: entry.key, - child: Text(entry.value.name), + value: entryKey, + child: Text(entry.name), ); }).toList(), onSaved: (value) { @@ -92,43 +127,45 @@ class _SupplyPageState extends State { const SizedBox(height: 16), // Origin Field with Dropdown and Text Input - AutocompletedTextField( - options: origins, - getValue: () => _selectedOrigin, - onChanged: (value) { - _selectedOrigin = value; - }, - onSelection: (String selection) async { - var price = _priceController.text.isEmpty - ? await API() - .getLatestPrice(widget.item.id, variant, - origin: selection) - .then((x) => x.value.toStringAsFixed(2)) - : _priceController.text; - setState(() { - _priceController.text = price; - _selectedOrigin = selection; - }); - }, - label: "Origin"), + if (widget.forceOrigin == null) + AutocompletedTextField( + options: origins, + getValue: () => _selectedOrigin, + onChanged: (value) { + _selectedOrigin = value; + }, + onSelection: (String selection) async { + var price = _priceController.text.isEmpty + ? await API() + .getLatestPrice(widget.item.id, variant, + origin: selection) + .then((x) => x.value.toStringAsFixed(2)) + : _priceController.text; + setState(() { + _priceController.text = price; + _selectedOrigin = selection; + }); + }, + label: "Origin"), const SizedBox(height: 16), // Price Field - TextFormField( - decoration: const InputDecoration(labelText: 'Price'), - keyboardType: TextInputType.number, - controller: _priceController, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter a price'; - } - if (double.tryParse(value) == null) { - return 'Please enter a valid number'; - } - return null; - }, - ), + if (widget.forcePrice == null) + TextFormField( + decoration: const InputDecoration(labelText: 'Price'), + keyboardType: TextInputType.number, + controller: _priceController, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a price'; + } + if (double.tryParse(value) == null) { + return 'Please enter a valid number'; + } + return null; + }, + ), const SizedBox(height: 16), @@ -253,3 +290,37 @@ class AutocompletedTextField extends StatelessWidget { ); } } + +class SupplyForm { + final String itemID; + final String variant; + final String price; + final String? origin; + final String? location; + final String note; + + factory SupplyForm.fromJson(Map json) { + return SupplyForm( + itemID: json['item'], + variant: json['variant'], + price: json['price'], + origin: json['origin'], + location: json['location'], + note: json['note'], + ); + } + + SupplyForm({ + required this.itemID, + required this.variant, + required this.price, + required this.origin, + required this.location, + required this.note, + }); + + Transaction transaction(Map locations) { + return Transaction.inMemory(itemID, variant, price, origin, + location != null ? locations[location!] : null, note); + } +} diff --git a/lib/pages/transaction.dart b/lib/pages/transaction.dart index a368875..b55fbbd 100644 --- a/lib/pages/transaction.dart +++ b/lib/pages/transaction.dart @@ -222,10 +222,11 @@ class TransactionCard extends StatelessWidget { ), ], ), - Text( - tsFormat(t.timestamp), - style: TextStyle(fontSize: 14, color: Colors.grey[700]), - ), + if (t.timestamp != 0) + Text( + tsFormat(t.timestamp), + style: TextStyle(fontSize: 14, color: Colors.grey[700]), + ), ], ), if ((t.note ?? "").isNotEmpty) ...[