inventree-manufacturing-costs 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,166 @@
1
+ """Custom model definitions for the ManufacturingCosts plugin."""
2
+
3
+ from decimal import Decimal
4
+
5
+ from django.contrib.auth.models import User
6
+ from django.db import models
7
+ from django.utils.translation import gettext_lazy as _
8
+
9
+ from djmoney.money import Money
10
+
11
+ import InvenTree.helpers
12
+ from InvenTree.fields import InvenTreeModelMoneyField
13
+
14
+
15
+ class ManufacturingRate(models.Model):
16
+ """Model to store manufacturing 'rates' for different processes."""
17
+
18
+ class Meta:
19
+ """Meta options for the model."""
20
+
21
+ app_label = "manufacturing_costs"
22
+
23
+ def __str__(self):
24
+ """String representation of the manufacturing rate."""
25
+ return self.name
26
+
27
+ name = models.CharField(
28
+ max_length=100,
29
+ unique=True,
30
+ verbose_name=_("Name"),
31
+ help_text=_("Name of the manufacturing rate"),
32
+ )
33
+
34
+ description = models.CharField(
35
+ max_length=200,
36
+ verbose_name=_("Description"),
37
+ help_text=_("Description of the manufacturing rate"),
38
+ )
39
+
40
+ # TODO: Implement custom validation for the 'units' field
41
+
42
+ units = models.CharField(
43
+ max_length=50,
44
+ verbose_name=_("Units"),
45
+ blank=True,
46
+ help_text=_("Units for the manufacturing rate"),
47
+ )
48
+
49
+ price = InvenTreeModelMoneyField(
50
+ max_digits=19,
51
+ decimal_places=6,
52
+ allow_negative=False,
53
+ verbose_name=_("Cost"),
54
+ help_text=_("Manufacturing rate cost"),
55
+ )
56
+
57
+
58
+ class ManufacturingCost(models.Model):
59
+ """Model to store manufacturing costs associated with a part."""
60
+
61
+ class Meta:
62
+ """Meta options for the model."""
63
+
64
+ app_label = "manufacturing_costs"
65
+
66
+ def save(self, *args, **kwargs):
67
+ """Custom save method."""
68
+
69
+ self.updated = InvenTree.helpers.current_time()
70
+ super().save(*args, **kwargs)
71
+
72
+ active = models.BooleanField(
73
+ default=True,
74
+ verbose_name=_("Active"),
75
+ help_text=_("Is this manufacturing cost active?"),
76
+ )
77
+
78
+ inherited = models.BooleanField(
79
+ default=False,
80
+ verbose_name=_("Inherited"),
81
+ help_text=_("Is this manufacturing cost inherited by variant parts?"),
82
+ )
83
+
84
+ part = models.ForeignKey(
85
+ "part.Part",
86
+ on_delete=models.CASCADE,
87
+ related_name="manufacturing_costs",
88
+ verbose_name=_("Part"),
89
+ help_text=_("The part associated with this manufacturing cost"),
90
+ )
91
+
92
+ rate = models.ForeignKey(
93
+ ManufacturingRate,
94
+ on_delete=models.CASCADE,
95
+ null=True,
96
+ blank=True,
97
+ related_name="manufacturing_costs",
98
+ verbose_name=_("Manufacturing Rate"),
99
+ help_text=_("The manufacturing rate used for this cost"),
100
+ )
101
+
102
+ quantity = models.DecimalField(
103
+ max_digits=19,
104
+ decimal_places=6,
105
+ default=1,
106
+ verbose_name=_("Quantity"),
107
+ help_text=_("Quantity of the part for which this cost applies"),
108
+ )
109
+
110
+ # Note: The unit cost will override the rate cost if provided
111
+ unit_cost = InvenTreeModelMoneyField(
112
+ max_digits=19,
113
+ decimal_places=6,
114
+ null=True,
115
+ blank=True,
116
+ allow_negative=False,
117
+ verbose_name=_("Cost"),
118
+ help_text=_("Cost of manufacturing this part"),
119
+ )
120
+
121
+ description = models.CharField(
122
+ max_length=200,
123
+ blank=True,
124
+ verbose_name=_("Description"),
125
+ help_text=_("Description of this manufacturing cost"),
126
+ )
127
+
128
+ notes = models.CharField(
129
+ max_length=200,
130
+ blank=True,
131
+ verbose_name=_("Notes"),
132
+ help_text=_("Additional notes about this manufacturing cost"),
133
+ )
134
+
135
+ updated = models.DateTimeField(
136
+ verbose_name=_("Updated"),
137
+ help_text=_("Timestamp of last update"),
138
+ default=None,
139
+ blank=True,
140
+ null=True,
141
+ )
142
+
143
+ updated_by = models.ForeignKey(
144
+ User,
145
+ on_delete=models.SET_NULL,
146
+ null=True,
147
+ blank=True,
148
+ related_name="%(class)s_updated",
149
+ verbose_name=_("Update By"),
150
+ help_text=_("User who last updated this object"),
151
+ )
152
+
153
+ def calculate_cost(self, quantity: Decimal) -> Money:
154
+ """Calculate the total manufacturing cost for a given quantity.
155
+
156
+ - If a 'rate' is specified, use the rate price.
157
+ - Otherwise, use the 'unit_cost' if specified.
158
+ """
159
+
160
+ if self.rate is not None:
161
+ return self.rate.price * quantity
162
+
163
+ elif self.unit_cost is not None:
164
+ return self.unit_cost * quantity
165
+
166
+ return Money(0, "USD") # Default to zero cost if neither is specified
@@ -0,0 +1,136 @@
1
+ """API serializers for the ManufacturingCosts plugin."""
2
+
3
+ from rest_framework import serializers
4
+
5
+
6
+ from InvenTree.serializers import (
7
+ InvenTreeCurrencySerializer,
8
+ InvenTreeDecimalField,
9
+ InvenTreeModelSerializer,
10
+ InvenTreeMoneySerializer,
11
+ )
12
+
13
+ import part.models as part_models
14
+ from part.serializers import PartBriefSerializer
15
+ from users.serializers import UserSerializer
16
+
17
+ from .models import ManufacturingRate, ManufacturingCost
18
+
19
+
20
+ class ManufacturingRateSerializer(InvenTreeModelSerializer):
21
+ """Serializer for the MachiningRate model."""
22
+
23
+ class Meta:
24
+ """Meta options for the serializer."""
25
+
26
+ model = ManufacturingRate
27
+ fields = [
28
+ "pk",
29
+ "name",
30
+ "description",
31
+ "units",
32
+ "price",
33
+ "price_currency",
34
+ ]
35
+
36
+ price = InvenTreeMoneySerializer()
37
+ price_currency = InvenTreeCurrencySerializer()
38
+
39
+
40
+ class ManufacturingCostSerializer(InvenTreeModelSerializer):
41
+ """Serializer for the ManufacturingCost model."""
42
+
43
+ class Meta:
44
+ """Meta options for the serializer."""
45
+
46
+ model = ManufacturingCost
47
+ fields = [
48
+ "pk",
49
+ "description",
50
+ "active",
51
+ "inherited",
52
+ "part",
53
+ "part_detail",
54
+ "rate",
55
+ "rate_detail",
56
+ "quantity",
57
+ "unit_cost",
58
+ "unit_cost_currency",
59
+ "notes",
60
+ "updated",
61
+ "updated_by",
62
+ "updated_by_detail",
63
+ ]
64
+
65
+ read_only_fields = ["updated", "updated_by"]
66
+
67
+ rate = serializers.PrimaryKeyRelatedField(
68
+ queryset=ManufacturingRate.objects.all(),
69
+ allow_null=True,
70
+ required=False,
71
+ )
72
+
73
+ quantity = InvenTreeDecimalField()
74
+
75
+ unit_cost = InvenTreeMoneySerializer(allow_null=True)
76
+ unit_cost_currency = InvenTreeCurrencySerializer()
77
+ part_detail = PartBriefSerializer(source="part", read_only=True, many=False)
78
+ rate_detail = ManufacturingRateSerializer(source="rate", read_only=True, many=False)
79
+ updated_by_detail = UserSerializer(
80
+ source="updated_by", many=False, read_only=True, allow_null=True
81
+ )
82
+
83
+ def validate(self, data):
84
+ """Validate the provided data."""
85
+
86
+ data = super().validate(data)
87
+
88
+ rate = data.get("rate", None)
89
+ unit_cost = data.get("unit_cost", None)
90
+
91
+ if rate is not None and unit_cost is not None:
92
+ msg = "Only one of 'rate' or 'unit_cost' should be specified"
93
+ raise serializers.ValidationError({
94
+ "rate": msg,
95
+ "unit_cost": msg,
96
+ })
97
+
98
+ return data
99
+
100
+ def save(self):
101
+ """Save the model instance."""
102
+
103
+ instance = super().save()
104
+
105
+ if request := self.context.get("request", None):
106
+ instance.updated_by = request.user
107
+ instance.save()
108
+
109
+ return instance
110
+
111
+
112
+ class AssemblyCostRequestSerializer(serializers.Serializer):
113
+ """Serializer for requesting manufacturing cost data for an assembly."""
114
+
115
+ part = serializers.PrimaryKeyRelatedField(
116
+ queryset=part_models.Part.objects.filter(assembly=True),
117
+ many=False,
118
+ required=True,
119
+ label="Assembly Part",
120
+ help_text="Select the assembly part for which to retrieve manufacturing costs",
121
+ )
122
+
123
+ include_subassemblies = serializers.BooleanField(
124
+ required=False,
125
+ default=True,
126
+ label="Include Sub-assemblies",
127
+ help_text="Include manufacturing costs for sub-assemblies",
128
+ )
129
+
130
+ export_format = serializers.ChoiceField(
131
+ choices=[("csv", "CSV"), ("xls", "XLS"), ("xlsx", "XLSX")],
132
+ required=False,
133
+ default="csv",
134
+ label="Export Format",
135
+ help_text="Select the format for exporting the manufacturing cost data",
136
+ )
@@ -0,0 +1,24 @@
1
+ {
2
+ "_index-BjwOiYns.js": {
3
+ "file": "assets/index-BjwOiYns.js",
4
+ "name": "index"
5
+ },
6
+ "src/AdminPanel.tsx": {
7
+ "file": "AdminPanel.js",
8
+ "name": "AdminPanel",
9
+ "src": "src/AdminPanel.tsx",
10
+ "isEntry": true,
11
+ "imports": [
12
+ "_index-BjwOiYns.js"
13
+ ]
14
+ },
15
+ "src/PartPanel.tsx": {
16
+ "file": "PartPanel.js",
17
+ "name": "PartPanel",
18
+ "src": "src/PartPanel.tsx",
19
+ "isEntry": true,
20
+ "imports": [
21
+ "_index-BjwOiYns.js"
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,192 @@
1
+ import { u as useQuery, a as apiUrl, R as RowEditAction, b as RowDuplicateAction, c as RowDeleteAction, f as formatCurrencyValue, d as RowActions, I as IconInfoCircle, e as IconExclamationCircle, A as AddItemButton, S as SearchInput, g as IconRefresh, q as qr, h as checkPluginVersion } from "./assets/index-BjwOiYns.js";
2
+ const ActionIcon = window["MantineCore"].ActionIcon;
3
+ const Alert = window["MantineCore"].Alert;
4
+ const Group = window["MantineCore"].Group;
5
+ const Stack = window["MantineCore"].Stack;
6
+ const Text = window["MantineCore"].Text;
7
+ const Tooltip = window["MantineCore"].Tooltip;
8
+ const useCallback = window["React"].useCallback;
9
+ const useMemo = window["React"].useMemo;
10
+ const useState = window["React"].useState;
11
+ function ManufacturingCostsAdminPanel({
12
+ context
13
+ }) {
14
+ const [searchTerm, setSearchTerm] = useState("");
15
+ const RATE_URL = "/plugin/manufacturing-costs/rate/";
16
+ const dataQuery = useQuery(
17
+ {
18
+ queryKey: ["manufacturing-rate", searchTerm],
19
+ queryFn: async () => {
20
+ var _a;
21
+ return (_a = context == null ? void 0 : context.api) == null ? void 0 : _a.get(RATE_URL, {
22
+ params: {
23
+ search: searchTerm
24
+ }
25
+ }).then((response) => response.data);
26
+ }
27
+ },
28
+ context.queryClient
29
+ );
30
+ const rateFields = useMemo(() => {
31
+ return {
32
+ name: {},
33
+ description: {},
34
+ price: {},
35
+ price_currency: {},
36
+ units: {}
37
+ };
38
+ }, []);
39
+ const [selectedRecord, setSelectedRecord] = useState(null);
40
+ const createRateForm = context.forms.create({
41
+ url: apiUrl(RATE_URL),
42
+ title: "Add Rate",
43
+ fields: rateFields,
44
+ successMessage: "Rate created",
45
+ onFormSuccess: () => {
46
+ dataQuery.refetch();
47
+ }
48
+ });
49
+ const editRateForm = context.forms.edit({
50
+ url: apiUrl(RATE_URL, selectedRecord == null ? void 0 : selectedRecord.pk),
51
+ title: "Edit Rate",
52
+ fields: rateFields,
53
+ successMessage: "Rate updated",
54
+ onFormSuccess: () => {
55
+ dataQuery.refetch();
56
+ }
57
+ });
58
+ const deleteRateForm = context.forms.delete({
59
+ url: apiUrl(RATE_URL, selectedRecord == null ? void 0 : selectedRecord.pk),
60
+ title: "Delete Rate",
61
+ successMessage: "Rate deleted",
62
+ onFormSuccess: () => {
63
+ dataQuery.refetch();
64
+ }
65
+ });
66
+ const duplicateRateForm = context.forms.create({
67
+ url: apiUrl(RATE_URL),
68
+ title: "Add Rate",
69
+ fields: rateFields,
70
+ initialData: {
71
+ ...selectedRecord
72
+ },
73
+ successMessage: "Rate created",
74
+ onFormSuccess: () => {
75
+ dataQuery.refetch();
76
+ }
77
+ });
78
+ const rowActions = useCallback((record) => {
79
+ return [
80
+ RowEditAction({
81
+ onClick: () => {
82
+ setSelectedRecord(record);
83
+ editRateForm == null ? void 0 : editRateForm.open();
84
+ }
85
+ }),
86
+ RowDuplicateAction({
87
+ onClick: () => {
88
+ setSelectedRecord(record);
89
+ duplicateRateForm == null ? void 0 : duplicateRateForm.open();
90
+ }
91
+ }),
92
+ RowDeleteAction({
93
+ onClick: () => {
94
+ setSelectedRecord(record);
95
+ deleteRateForm == null ? void 0 : deleteRateForm.open();
96
+ }
97
+ })
98
+ ];
99
+ }, []);
100
+ const dataColumns = useMemo(() => {
101
+ return [
102
+ {
103
+ accessor: "name",
104
+ sortable: true
105
+ },
106
+ {
107
+ accessor: "description"
108
+ },
109
+ {
110
+ accessor: "price",
111
+ title: "Rate",
112
+ sortable: true,
113
+ render: (record) => {
114
+ return /* @__PURE__ */ React.createElement(Group, { gap: "sm" }, /* @__PURE__ */ React.createElement(Text, null, formatCurrencyValue(record.price, {
115
+ currency: record.price_currency
116
+ })), record.units && /* @__PURE__ */ React.createElement(Text, { size: "xs" }, "[", record.units, "]"));
117
+ }
118
+ },
119
+ {
120
+ accessor: "---",
121
+ title: " ",
122
+ width: 50,
123
+ resizable: false,
124
+ sortable: false,
125
+ render: (record, index) => /* @__PURE__ */ React.createElement(RowActions, { actions: rowActions(record), index })
126
+ }
127
+ ];
128
+ }, []);
129
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, createRateForm == null ? void 0 : createRateForm.modal, editRateForm == null ? void 0 : editRateForm.modal, duplicateRateForm == null ? void 0 : duplicateRateForm.modal, deleteRateForm == null ? void 0 : deleteRateForm.modal, /* @__PURE__ */ React.createElement(Stack, { gap: "xs" }, /* @__PURE__ */ React.createElement(
130
+ Alert,
131
+ {
132
+ color: "blue",
133
+ icon: /* @__PURE__ */ React.createElement(IconInfoCircle, null),
134
+ title: "Manufacturing Rates"
135
+ },
136
+ "Predefined rates for different manufaucturing processes. These can be referenced to assign manufaucturing costs to parts."
137
+ ), dataQuery.isError && /* @__PURE__ */ React.createElement(
138
+ Alert,
139
+ {
140
+ color: "red",
141
+ title: "Error Fetching Data",
142
+ icon: /* @__PURE__ */ React.createElement(IconExclamationCircle, null)
143
+ },
144
+ dataQuery.error instanceof Error ? dataQuery.error.message : "An error occurred while fetching data from the server."
145
+ ), /* @__PURE__ */ React.createElement(Group, { justify: "space-between" }, /* @__PURE__ */ React.createElement(Group, { gap: "xs" }, /* @__PURE__ */ React.createElement(
146
+ AddItemButton,
147
+ {
148
+ tooltip: "Add new rate",
149
+ onClick: () => {
150
+ createRateForm == null ? void 0 : createRateForm.open();
151
+ }
152
+ }
153
+ )), /* @__PURE__ */ React.createElement(Group, { gap: "xs" }, /* @__PURE__ */ React.createElement(
154
+ SearchInput,
155
+ {
156
+ searchCallback: (value) => {
157
+ setSearchTerm(value);
158
+ }
159
+ }
160
+ ), /* @__PURE__ */ React.createElement(Tooltip, { label: "Refresh data", position: "top-end" }, /* @__PURE__ */ React.createElement(
161
+ ActionIcon,
162
+ {
163
+ variant: "transparent",
164
+ onClick: () => {
165
+ dataQuery.refetch();
166
+ }
167
+ },
168
+ /* @__PURE__ */ React.createElement(IconRefresh, null)
169
+ )))), /* @__PURE__ */ React.createElement(
170
+ qr,
171
+ {
172
+ minHeight: 250,
173
+ withTableBorder: true,
174
+ withColumnBorders: true,
175
+ idAccessor: "pk",
176
+ noRecordsText: "No manufacturing rates found",
177
+ records: dataQuery.data || [],
178
+ fetching: dataQuery.isFetching || dataQuery.isLoading,
179
+ columns: dataColumns,
180
+ pinLastColumn: true
181
+ }
182
+ )));
183
+ }
184
+ function renderAdminPanel(context) {
185
+ checkPluginVersion(context);
186
+ return /* @__PURE__ */ React.createElement(ManufacturingCostsAdminPanel, { context });
187
+ }
188
+ export {
189
+ ManufacturingCostsAdminPanel,
190
+ renderAdminPanel
191
+ };
192
+ //# sourceMappingURL=AdminPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminPanel.js","sources":["../../frontend/src/AdminPanel.tsx"],"sourcesContent":["// Import for type checking\nimport {\n AddItemButton,\n apiUrl,\n checkPluginVersion,\n formatCurrencyValue,\n type InvenTreePluginContext,\n RowActions,\n RowDeleteAction,\n RowDuplicateAction,\n RowEditAction,\n SearchInput\n} from '@inventreedb/ui';\nimport { ActionIcon, Alert, Group, Stack, Text, Tooltip } from '@mantine/core';\nimport {\n IconExclamationCircle,\n IconInfoCircle,\n IconRefresh\n} from '@tabler/icons-react';\nimport { useQuery } from '@tanstack/react-query';\nimport { DataTable } from 'mantine-datatable';\n\nimport { useCallback, useMemo, useState } from 'react';\n\nexport function ManufacturingCostsAdminPanel({\n context\n}: {\n context: InvenTreePluginContext;\n}) {\n const [searchTerm, setSearchTerm] = useState<string>('');\n\n const RATE_URL: string = '/plugin/manufacturing-costs/rate/';\n\n // Fetch manufacturing rates from the API\n const dataQuery = useQuery(\n {\n queryKey: ['manufacturing-rate', searchTerm],\n queryFn: async () => {\n return context?.api\n ?.get(RATE_URL, {\n params: {\n search: searchTerm\n }\n })\n .then((response: any) => response.data);\n }\n },\n context.queryClient\n );\n\n const rateFields: any = useMemo(() => {\n return {\n name: {},\n description: {},\n price: {},\n price_currency: {},\n units: {}\n };\n }, []);\n\n // Record which is selected in the table\n const [selectedRecord, setSelectedRecord] = useState<any>(null);\n\n // Modal form to create a new rate\n const createRateForm = context.forms.create({\n url: apiUrl(RATE_URL),\n title: 'Add Rate',\n fields: rateFields,\n successMessage: 'Rate created',\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n // Modal form to edit the selected rate\n const editRateForm = context.forms.edit({\n url: apiUrl(RATE_URL, selectedRecord?.pk),\n title: 'Edit Rate',\n fields: rateFields,\n successMessage: 'Rate updated',\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n // Modal form to delete the selected rate\n const deleteRateForm = context.forms.delete({\n url: apiUrl(RATE_URL, selectedRecord?.pk),\n title: 'Delete Rate',\n successMessage: 'Rate deleted',\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n // Modal form to duplicate the selected rate\n const duplicateRateForm = context.forms.create({\n url: apiUrl(RATE_URL),\n title: 'Add Rate',\n fields: rateFields,\n initialData: {\n ...selectedRecord\n },\n successMessage: 'Rate created',\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n // Render the actions available for a given row in the table\n const rowActions = useCallback((record: any) => {\n return [\n RowEditAction({\n onClick: () => {\n setSelectedRecord(record);\n editRateForm?.open();\n }\n }),\n RowDuplicateAction({\n onClick: () => {\n setSelectedRecord(record);\n duplicateRateForm?.open();\n }\n }),\n RowDeleteAction({\n onClick: () => {\n setSelectedRecord(record);\n deleteRateForm?.open();\n }\n })\n ];\n }, []);\n\n const dataColumns: any[] = useMemo(() => {\n return [\n {\n accessor: 'name',\n sortable: true\n },\n {\n accessor: 'description'\n },\n {\n accessor: 'price',\n title: 'Rate',\n sortable: true,\n render: (record: any) => {\n return (\n <Group gap='sm'>\n <Text>\n {formatCurrencyValue(record.price, {\n currency: record.price_currency\n })}\n </Text>\n {record.units && <Text size='xs'>[{record.units}]</Text>}\n </Group>\n );\n }\n },\n {\n accessor: '---',\n title: ' ',\n width: 50,\n resizable: false,\n sortable: false,\n render: (record: any, index: number) => (\n <RowActions actions={rowActions(record)} index={index} />\n )\n }\n ];\n }, []);\n\n return (\n <>\n {createRateForm?.modal}\n {editRateForm?.modal}\n {duplicateRateForm?.modal}\n {deleteRateForm?.modal}\n <Stack gap='xs'>\n <Alert\n color='blue'\n icon={<IconInfoCircle />}\n title={'Manufacturing Rates'}\n >\n Predefined rates for different manufaucturing processes. These can be\n referenced to assign manufaucturing costs to parts.\n </Alert>\n {dataQuery.isError && (\n <Alert\n color='red'\n title='Error Fetching Data'\n icon={<IconExclamationCircle />}\n >\n {dataQuery.error instanceof Error\n ? dataQuery.error.message\n : 'An error occurred while fetching data from the server.'}\n </Alert>\n )}\n <Group justify='space-between'>\n <Group gap='xs'>\n <AddItemButton\n tooltip='Add new rate'\n onClick={() => {\n createRateForm?.open();\n }}\n />\n </Group>\n <Group gap='xs'>\n <SearchInput\n searchCallback={(value: string) => {\n setSearchTerm(value);\n }}\n />\n <Tooltip label='Refresh data' position='top-end'>\n <ActionIcon\n variant='transparent'\n onClick={() => {\n dataQuery.refetch();\n }}\n >\n <IconRefresh />\n </ActionIcon>\n </Tooltip>\n </Group>\n </Group>\n <DataTable\n minHeight={250}\n withTableBorder\n withColumnBorders\n idAccessor={'pk'}\n noRecordsText='No manufacturing rates found'\n records={dataQuery.data || []}\n fetching={dataQuery.isFetching || dataQuery.isLoading}\n columns={dataColumns}\n pinLastColumn\n />\n </Stack>\n </>\n );\n}\n\n// This is the function which is called by InvenTree to render the actual panel component\nexport function renderAdminPanel(context: InvenTreePluginContext) {\n checkPluginVersion(context);\n\n return <ManufacturingCostsAdminPanel context={context} />;\n}\n"],"names":["DataTable"],"mappings":";AAaA,MAAA,aAAA,OAAA,aAAA,EAAA;;;;;;AASA,MAAA,cAAA,OAAA,OAAA,EAAA;;;AAEO,SAAS,6BAA6B;AAAA,EAC3C;AACF,GAEG;AACD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAiB,EAAE;AAEvD,QAAM,WAAmB;AAGzB,QAAM,YAAY;AAAA,IAChB;AAAA,MACE,UAAU,CAAC,sBAAsB,UAAU;AAAA,MAC3C,SAAS,YAAY;;AACnB,gBAAO,wCAAS,QAAT,mBACH,IAAI,UAAU;AAAA,UACd,QAAQ;AAAA,YACN,QAAQ;AAAA,UAAA;AAAA,QACV,GAED,KAAK,CAAC,aAAkB,SAAS;AAAA,MACtC;AAAA,IAAA;AAAA,IAEF,QAAQ;AAAA,EAAA;AAGV,QAAM,aAAkB,QAAQ,MAAM;AACpC,WAAO;AAAA,MACL,MAAM,CAAA;AAAA,MACN,aAAa,CAAA;AAAA,MACb,OAAO,CAAA;AAAA,MACP,gBAAgB,CAAA;AAAA,MAChB,OAAO,CAAA;AAAA,IAAC;AAAA,EAEZ,GAAG,CAAA,CAAE;AAGL,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAc,IAAI;AAG9D,QAAM,iBAAiB,QAAQ,MAAM,OAAO;AAAA,IAC1C,KAAK,OAAO,QAAQ;AAAA,IACpB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,eAAe,MAAM;AACnB,gBAAU,QAAA;AAAA,IACZ;AAAA,EAAA,CACD;AAGD,QAAM,eAAe,QAAQ,MAAM,KAAK;AAAA,IACtC,KAAK,OAAO,UAAU,iDAAgB,EAAE;AAAA,IACxC,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,eAAe,MAAM;AACnB,gBAAU,QAAA;AAAA,IACZ;AAAA,EAAA,CACD;AAGD,QAAM,iBAAiB,QAAQ,MAAM,OAAO;AAAA,IAC1C,KAAK,OAAO,UAAU,iDAAgB,EAAE;AAAA,IACxC,OAAO;AAAA,IACP,gBAAgB;AAAA,IAChB,eAAe,MAAM;AACnB,gBAAU,QAAA;AAAA,IACZ;AAAA,EAAA,CACD;AAGD,QAAM,oBAAoB,QAAQ,MAAM,OAAO;AAAA,IAC7C,KAAK,OAAO,QAAQ;AAAA,IACpB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,aAAa;AAAA,MACX,GAAG;AAAA,IAAA;AAAA,IAEL,gBAAgB;AAAA,IAChB,eAAe,MAAM;AACnB,gBAAU,QAAA;AAAA,IACZ;AAAA,EAAA,CACD;AAGD,QAAM,aAAa,YAAY,CAAC,WAAgB;AAC9C,WAAO;AAAA,MACL,cAAc;AAAA,QACZ,SAAS,MAAM;AACb,4BAAkB,MAAM;AACxB,uDAAc;AAAA,QAChB;AAAA,MAAA,CACD;AAAA,MACD,mBAAmB;AAAA,QACjB,SAAS,MAAM;AACb,4BAAkB,MAAM;AACxB,iEAAmB;AAAA,QACrB;AAAA,MAAA,CACD;AAAA,MACD,gBAAgB;AAAA,QACd,SAAS,MAAM;AACb,4BAAkB,MAAM;AACxB,2DAAgB;AAAA,QAClB;AAAA,MAAA,CACD;AAAA,IAAA;AAAA,EAEL,GAAG,CAAA,CAAE;AAEL,QAAM,cAAqB,QAAQ,MAAM;AACvC,WAAO;AAAA,MACL;AAAA,QACE,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA,MAEZ;AAAA,QACE,UAAU;AAAA,MAAA;AAAA,MAEZ;AAAA,QACE,UAAU;AAAA,QACV,OAAO;AAAA,QACP,UAAU;AAAA,QACV,QAAQ,CAAC,WAAgB;AACvB,iBACE,sBAAA,cAAC,SAAM,KAAI,KAAA,uCACR,MAAA,MACE,oBAAoB,OAAO,OAAO;AAAA,YACjC,UAAU,OAAO;AAAA,UAAA,CAClB,CACH,GACC,OAAO,SAAS,sBAAA,cAAC,MAAA,EAAK,MAAK,KAAA,GAAK,KAAE,OAAO,OAAM,GAAC,CACnD;AAAA,QAEJ;AAAA,MAAA;AAAA,MAEF;AAAA,QACE,UAAU;AAAA,QACV,OAAO;AAAA,QACP,OAAO;AAAA,QACP,WAAW;AAAA,QACX,UAAU;AAAA,QACV,QAAQ,CAAC,QAAa,UACpB,sBAAA,cAAC,cAAW,SAAS,WAAW,MAAM,GAAG,MAAA,CAAc;AAAA,MAAA;AAAA,IAE3D;AAAA,EAEJ,GAAG,CAAA,CAAE;AAEL,SACE,sBAAA,cAAA,MAAA,UAAA,MACG,iDAAgB,OAChB,6CAAc,OACd,uDAAmB,OACnB,iDAAgB,OACjB,sBAAA,cAAC,OAAA,EAAM,KAAI,QACT,sBAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,0CAAO,gBAAA,IAAe;AAAA,MACtB,OAAO;AAAA,IAAA;AAAA,IACR;AAAA,EAAA,GAIA,UAAU,WACT,sBAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAM;AAAA,MACN,0CAAO,uBAAA,IAAsB;AAAA,IAAA;AAAA,IAE5B,UAAU,iBAAiB,QACxB,UAAU,MAAM,UAChB;AAAA,EAAA,uCAGP,OAAA,EAAM,SAAQ,mBACb,sBAAA,cAAC,OAAA,EAAM,KAAI,KAAA,GACT,sBAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,MAAM;AACb,yDAAgB;AAAA,MAClB;AAAA,IAAA;AAAA,EAAA,CAEJ,GACA,sBAAA,cAAC,OAAA,EAAM,KAAI,KAAA,GACT,sBAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,gBAAgB,CAAC,UAAkB;AACjC,sBAAc,KAAK;AAAA,MACrB;AAAA,IAAA;AAAA,EAAA,GAEF,sBAAA,cAAC,SAAA,EAAQ,OAAM,gBAAe,UAAS,aACrC,sBAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,MAAM;AACb,kBAAU,QAAA;AAAA,MACZ;AAAA,IAAA;AAAA,wCAEC,aAAA,IAAY;AAAA,EAAA,CAEjB,CACF,CACF,GACA,sBAAA;AAAA,IAACA;AAAAA,IAAA;AAAA,MACC,WAAW;AAAA,MACX,iBAAe;AAAA,MACf,mBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,eAAc;AAAA,MACd,SAAS,UAAU,QAAQ,CAAA;AAAA,MAC3B,UAAU,UAAU,cAAc,UAAU;AAAA,MAC5C,SAAS;AAAA,MACT,eAAa;AAAA,IAAA;AAAA,EAAA,CAEjB,CACF;AAEJ;AAGO,SAAS,iBAAiB,SAAiC;AAChE,qBAAmB,OAAO;AAE1B,SAAO,sBAAA,cAAC,gCAA6B,QAAA,CAAkB;AACzD;"}