import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; import "package:inventree/api.dart"; import "package:inventree/helpers.dart"; import "package:inventree/inventree/company.dart"; import "package:inventree/inventree/model.dart"; import "package:inventree/inventree/orders.dart"; import "package:inventree/widget/order/extra_line_detail.dart"; import "package:inventree/widget/order/purchase_order_detail.dart"; import "package:inventree/widget/progress.dart"; import "package:inventree/api_form.dart"; import "package:inventree/l10.dart"; /* * Class representing an individual PurchaseOrder instance */ class InvenTreePurchaseOrder extends InvenTreeOrder { InvenTreePurchaseOrder() : super(); InvenTreePurchaseOrder.fromJson(Map json) : super.fromJson(json); @override InvenTreeModel createFromJson(Map json) => InvenTreePurchaseOrder.fromJson(json); @override String get URL => "order/po/"; @override Future goToDetailPage(BuildContext context) async { return Navigator.push( context, MaterialPageRoute(builder: (context) => PurchaseOrderDetailWidget(this)), ); } static const String MODEL_TYPE = "purchaseorder"; @override List get rolesRequired => ["purchase_order"]; String get receive_url => "${url}receive/"; @override Map> formFields() { Map> fields = { "reference": {}, "supplier": { "filters": {"is_supplier": true}, }, "supplier_reference": {}, "description": {}, "project_code": {}, "destination": {}, "start_date": {}, "target_date": {}, "link": {}, "responsible": {}, "contact": { "filters": {"company": supplierId}, }, }; if (!InvenTreeAPI().supportsProjectCodes) { fields.remove("project_code"); } if (!InvenTreeAPI().supportsPurchaseOrderDestination) { fields.remove("destination"); } if (!InvenTreeAPI().supportsStartDate) { fields.remove("start_date"); } return fields; } @override Map defaultFilters() { return {"supplier_detail": "true"}; } int get supplierId => getInt("supplier"); InvenTreeCompany? get supplier { dynamic supplier_detail = jsondata["supplier_detail"]; if (supplier_detail == null) { return null; } else { return InvenTreeCompany.fromJson(supplier_detail as Map); } } String get supplierReference => getString("supplier_reference"); int get destinationId => getInt("destination"); bool get isOpen => api.PurchaseOrderStatus.isNameIn(status, [ "PENDING", "PLACED", "ON_HOLD", ]); bool get isPending => api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]); bool get isPlaced => api.PurchaseOrderStatus.isNameIn(status, ["PLACED"]); bool get isFailed => api.PurchaseOrderStatus.isNameIn(status, [ "CANCELLED", "LOST", "RETURNED", ]); Future> getLineItems() async { final results = await InvenTreePOLineItem().list( filters: {"order": "${pk}"}, ); List items = []; for (var result in results) { if (result is InvenTreePOLineItem) { items.add(result); } } return items; } /// Mark this order as "placed" / "issued" Future issueOrder() async { // Order can only be placed when the order is 'pending' if (!isPending) { return; } showLoadingOverlay(); await api.post("${url}issue/", expectedStatusCode: 201); hideLoadingOverlay(); } /// Mark this order as "cancelled" Future cancelOrder() async { if (!isOpen) { return; } showLoadingOverlay(); await api.post("${url}cancel/", expectedStatusCode: 201); hideLoadingOverlay(); } } class InvenTreePOLineItem extends InvenTreeOrderLine { InvenTreePOLineItem() : super(); InvenTreePOLineItem.fromJson(Map json) : super.fromJson(json); @override InvenTreeModel createFromJson(Map json) => InvenTreePOLineItem.fromJson(json); @override String get URL => "order/po-line/"; @override List get rolesRequired => ["purchase_order"]; @override Map> formFields() { return { "part": { // We cannot edit the supplier part field here "hidden": true, }, "order": { // We cannot edit the order field here "hidden": true, }, "reference": {}, "quantity": {}, "purchase_price": {}, "purchase_price_currency": {}, "destination": {}, "notes": {}, "link": {}, }; } @override Map defaultFilters() { return {"part_detail": "true", "order_detail": "true"}; } double get received => getDouble("received"); bool get isComplete => received >= quantity; double get progressRatio { if (quantity <= 0 || received <= 0) { return 0; } return received / quantity; } String get progressString => simpleNumberString(received) + " / " + simpleNumberString(quantity); double get outstanding => quantity - received; int get supplierPartId => getInt("part"); InvenTreeSupplierPart? get supplierPart { dynamic detail = jsondata["supplier_part_detail"]; if (detail == null) { return null; } else { return InvenTreeSupplierPart.fromJson(detail as Map); } } InvenTreePurchaseOrder? get purchaseOrder { dynamic detail = jsondata["order_detail"]; if (detail == null) { return null; } else { return InvenTreePurchaseOrder.fromJson(detail as Map); } } String get SKU => getString("SKU", subKey: "supplier_part_detail"); double get purchasePrice => getDouble("purchase_price"); String get purchasePriceCurrency => getString("purchase_price_currency"); int get destinationId => getInt("destination"); Map get orderDetail => getMap("order_detail"); Map get destinationDetail => getMap("destination_detail"); // Receive this line item into stock Future receive( BuildContext context, { int? destination, double? quantity, String? barcode, Function? onSuccess, }) async { // Infer the destination location from the line item if not provided if (destinationId > 0) { destination = destinationId; } destination ??= (orderDetail["destination"]) as int?; quantity ??= outstanding; // Construct form fields Map fields = { "line_item": { "parent": "items", "nested": true, "hidden": true, "value": pk, }, "quantity": {"parent": "items", "nested": true, "value": quantity}, "location": {}, "status": {"parent": "items", "nested": true}, "batch_code": {"parent": "items", "nested": true}, "barcode": { "parent": "items", "nested": true, "type": "barcode", "label": L10().barcodeAssign, "value": barcode, "required": false, }, }; if (destination != null && destination > 0) { fields["location"]?["value"] = destination; } InvenTreePurchaseOrder? order = purchaseOrder; if (order != null) { await launchApiForm( context, L10().receiveItem, order.receive_url, fields, method: "POST", icon: TablerIcons.transition_right, onSuccess: (data) { if (onSuccess != null) { onSuccess(); } }, ); } } } class InvenTreePOExtraLineItem extends InvenTreeExtraLineItem { InvenTreePOExtraLineItem() : super(); InvenTreePOExtraLineItem.fromJson(Map json) : super.fromJson(json); @override InvenTreeModel createFromJson(Map json) => InvenTreePOExtraLineItem.fromJson(json); @override String get URL => "order/po-extra-line/"; @override List get rolesRequired => ["purchase_order"]; @override Future goToDetailPage(BuildContext context) async { return Navigator.push( context, MaterialPageRoute(builder: (context) => ExtraLineDetailWidget(this)), ); } }