2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-05-02 15:28:53 +00:00
inventree-app/lib/inventree/purchase_order.dart
Oliver 524c5469f1
[refactor] Scan improvements (#577)
* Handle error on unexpected barcode response

* Add ManufacturerPart detail view

* Support barcode scanning for manufacturer part

* Refactoring for null checks

* Ignore selected errors in sentry

* Fix API implementation for ManufacturerPart

* Update release notes

* More error handling

* Decode quantity betterer

* Refactoring

* Add option to confirm checkin details

* Improve response handlign

* Cleanup

* Remove unused imports

* Fix async function

* Fix for assigning custom barcode

* Handle barcode scan result for company

* Fix

* Adjust scan priority

* Refactoring MODEL_TYPE

- Use instead of duplicated const strings

* @override fix
2024-12-14 15:24:23 +11:00

330 lines
8.0 KiB
Dart

import "package:flutter/cupertino.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/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<String, dynamic> json) : super.fromJson(json);
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrder.fromJson(json);
@override
String get URL => "order/po/";
static const String MODEL_TYPE = "purchaseorder";
@override
List<String> get rolesRequired => ["purchase_order"];
String get receive_url => "${url}receive/";
@override
Map<String, Map<String, dynamic>> formFields() {
Map<String, Map<String, dynamic>> fields = {
"reference": {},
"supplier": {
"filters": {
"is_supplier": true,
},
},
"supplier_reference": {},
"description": {},
"project_code": {},
"destination": {},
"target_date": {},
"link": {},
"responsible": {},
"contact": {
"filters": {
"company": supplierId,
}
},
};
if (!InvenTreeAPI().supportsProjectCodes) {
fields.remove("project_code");
}
if (!InvenTreeAPI().supportsPurchaseOrderDestination) {
fields.remove("destination");
}
return fields;
}
@override
Map<String, String> 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, dynamic>);
}
}
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<List<InvenTreePOLineItem>> getLineItems() async {
final results = await InvenTreePOLineItem().list(
filters: {
"order": "${pk}",
}
);
List<InvenTreePOLineItem> items = [];
for (var result in results) {
if (result is InvenTreePOLineItem) {
items.add(result);
}
}
return items;
}
/// Mark this order as "placed" / "issued"
Future<void> 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<void> cancelOrder() async {
if (!isOpen) {
return;
}
showLoadingOverlay();
await api.post("${url}cancel/", expectedStatusCode: 201);
hideLoadingOverlay();
}
}
class InvenTreePOLineItem extends InvenTreeOrderLine {
InvenTreePOLineItem() : super();
InvenTreePOLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePOLineItem.fromJson(json);
@override
String get URL => "order/po-line/";
@override
List<String> get rolesRequired => ["purchase_order"];
@override
Map<String, Map<String, dynamic>> 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<String, String> 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<String, dynamic>);
}
}
InvenTreePurchaseOrder? get purchaseOrder {
dynamic detail = jsondata["order_detail"];
if (detail == null) {
return null;
} else {
return InvenTreePurchaseOrder.fromJson(detail as Map<String, dynamic>);
}
}
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<String, dynamic> get orderDetail => getMap("order_detail");
Map<String, dynamic> get destinationDetail => getMap("destination_detail");
// Receive this line item into stock
Future<void> 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<String, dynamic> 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 representing an attachment file against a PurchaseOrder object
*/
class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment {
InvenTreePurchaseOrderAttachment() : super();
InvenTreePurchaseOrderAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
@override
String get REFERENCE_FIELD => "order";
@override
String get REF_MODEL_TYPE => "purchaseorder";
@override
String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "order/po/attachment/";
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrderAttachment.fromJson(json);
}