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,333 @@
1
+ import { i as createReactComponent, h as checkPluginVersion, a as apiUrl, u as useQuery, R as RowEditAction, b as RowDuplicateAction, c as RowDeleteAction, j as formatDecimal, f as formatCurrencyValue, I as IconInfoCircle, d as RowActions, e as IconExclamationCircle, A as AddItemButton, S as SearchInput, g as IconRefresh, q as qr } from "./assets/index-BjwOiYns.js";
2
+ var ModelType = /* @__PURE__ */ ((ModelType2) => {
3
+ ModelType2["part"] = "part";
4
+ ModelType2["supplierpart"] = "supplierpart";
5
+ ModelType2["manufacturerpart"] = "manufacturerpart";
6
+ ModelType2["partcategory"] = "partcategory";
7
+ ModelType2["partparametertemplate"] = "partparametertemplate";
8
+ ModelType2["parttesttemplate"] = "parttesttemplate";
9
+ ModelType2["projectcode"] = "projectcode";
10
+ ModelType2["stockitem"] = "stockitem";
11
+ ModelType2["stocklocation"] = "stocklocation";
12
+ ModelType2["stocklocationtype"] = "stocklocationtype";
13
+ ModelType2["stockhistory"] = "stockhistory";
14
+ ModelType2["build"] = "build";
15
+ ModelType2["buildline"] = "buildline";
16
+ ModelType2["builditem"] = "builditem";
17
+ ModelType2["company"] = "company";
18
+ ModelType2["purchaseorder"] = "purchaseorder";
19
+ ModelType2["purchaseorderlineitem"] = "purchaseorderlineitem";
20
+ ModelType2["salesorder"] = "salesorder";
21
+ ModelType2["salesordershipment"] = "salesordershipment";
22
+ ModelType2["returnorder"] = "returnorder";
23
+ ModelType2["returnorderlineitem"] = "returnorderlineitem";
24
+ ModelType2["importsession"] = "importsession";
25
+ ModelType2["address"] = "address";
26
+ ModelType2["contact"] = "contact";
27
+ ModelType2["owner"] = "owner";
28
+ ModelType2["user"] = "user";
29
+ ModelType2["group"] = "group";
30
+ ModelType2["reporttemplate"] = "reporttemplate";
31
+ ModelType2["labeltemplate"] = "labeltemplate";
32
+ ModelType2["pluginconfig"] = "pluginconfig";
33
+ ModelType2["contenttype"] = "contenttype";
34
+ ModelType2["selectionlist"] = "selectionlist";
35
+ ModelType2["error"] = "error";
36
+ return ModelType2;
37
+ })(ModelType || {});
38
+ /**
39
+ * @license @tabler/icons-react v3.34.1 - MIT
40
+ *
41
+ * This source code is licensed under the MIT license.
42
+ * See the LICENSE file in the root directory of this source tree.
43
+ */
44
+ const __iconNode$1 = [["path", { "d": "M14 3v4a1 1 0 0 0 1 1h4", "key": "svg-0" }], ["path", { "d": "M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z", "key": "svg-1" }], ["path", { "d": "M12 17v-6", "key": "svg-2" }], ["path", { "d": "M9.5 14.5l2.5 2.5l2.5 -2.5", "key": "svg-3" }]];
45
+ const IconFileDownload = createReactComponent("outline", "file-download", "FileDownload", __iconNode$1);
46
+ /**
47
+ * @license @tabler/icons-react v3.34.1 - MIT
48
+ *
49
+ * This source code is licensed under the MIT license.
50
+ * See the LICENSE file in the root directory of this source tree.
51
+ */
52
+ const __iconNode = [["path", { "d": "M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0", "key": "svg-0" }], ["path", { "d": "M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2", "key": "svg-1" }]];
53
+ const IconUser = createReactComponent("outline", "user", "User", __iconNode);
54
+ const ActionIcon = window["MantineCore"].ActionIcon;
55
+ const Alert = window["MantineCore"].Alert;
56
+ const Button = window["MantineCore"].Button;
57
+ const Group = window["MantineCore"].Group;
58
+ const HoverCard = window["MantineCore"].HoverCard;
59
+ const Menu = window["MantineCore"].Menu;
60
+ const Stack = window["MantineCore"].Stack;
61
+ const Text = window["MantineCore"].Text;
62
+ const Tooltip = window["MantineCore"].Tooltip;
63
+ const useCallback = window["React"].useCallback;
64
+ const useMemo = window["React"].useMemo;
65
+ const useState = window["React"].useState;
66
+ function RenderRate({ instance }) {
67
+ return /* @__PURE__ */ React.createElement(Group, { gap: "xs", justify: "space-between" }, /* @__PURE__ */ React.createElement(Text, null, instance.name), /* @__PURE__ */ React.createElement(Group, { gap: "xs", justify: "right" }, /* @__PURE__ */ React.createElement(Text, { size: "xs" }, instance.description), instance.units && /* @__PURE__ */ React.createElement(Text, { size: "xs" }, "[", instance.units, "]")));
68
+ }
69
+ function ManufacturingCostsPanel({
70
+ context
71
+ }) {
72
+ const partId = useMemo(() => {
73
+ return context.model == ModelType.part ? context.id || null : null;
74
+ }, [context.model, context.id]);
75
+ const [searchTerm, setSearchTerm] = useState("");
76
+ const RATE_URL = "/plugin/manufacturing-costs/rate/";
77
+ const COST_URL = "/plugin/manufacturing-costs/cost/";
78
+ const EXPORT_URL = "/plugin/manufacturing-costs/cost/export/";
79
+ const downloadData = useCallback(
80
+ (exportFormat) => {
81
+ if (!partId) {
82
+ return;
83
+ }
84
+ let url = `${apiUrl(EXPORT_URL)}?part=${partId}&export_format=${exportFormat}`;
85
+ if (context.host) {
86
+ url = `${context.host}${url}`;
87
+ } else {
88
+ url = `${window.location.origin}${url}`;
89
+ }
90
+ window.open(url, "_blank");
91
+ },
92
+ [partId, context.host, window.location]
93
+ );
94
+ const dataQuery = useQuery(
95
+ {
96
+ queryKey: ["manufacturing-cost", partId, searchTerm],
97
+ queryFn: async () => {
98
+ var _a;
99
+ const url = `${COST_URL}`;
100
+ return ((_a = context.api) == null ? void 0 : _a.get(url, {
101
+ params: {
102
+ part: partId,
103
+ search: searchTerm
104
+ }
105
+ }).then((response) => response.data).catch(() => [])) ?? [];
106
+ }
107
+ },
108
+ context.queryClient
109
+ );
110
+ const [selectedRecord, setSelectedRecord] = useState(null);
111
+ const costFields = useMemo(() => {
112
+ return {
113
+ part: {
114
+ value: partId,
115
+ disabled: true
116
+ },
117
+ description: {},
118
+ quantity: {},
119
+ // TODO: Add a "pre-field" element here
120
+ rate: {
121
+ api_url: apiUrl(RATE_URL),
122
+ modelRenderer: RenderRate
123
+ },
124
+ // TODO: Mark unit_cost and unit_cost currency as "disabled" if a rate is selected
125
+ unit_cost: {},
126
+ unit_cost_currency: {},
127
+ notes: {},
128
+ inherited: {},
129
+ active: {}
130
+ };
131
+ }, []);
132
+ const createCostForm = context.forms.create({
133
+ url: apiUrl(COST_URL),
134
+ title: "Add Manufacturing Cost",
135
+ fields: costFields,
136
+ successMessage: "Cost created",
137
+ onFormSuccess: () => {
138
+ dataQuery.refetch();
139
+ }
140
+ });
141
+ const duplicateCostForm = context.forms.create({
142
+ url: apiUrl(COST_URL),
143
+ title: "Add Manufacturing Cost",
144
+ fields: costFields,
145
+ successMessage: "Cost created",
146
+ initialData: selectedRecord,
147
+ onFormSuccess: () => {
148
+ dataQuery.refetch();
149
+ }
150
+ });
151
+ const editCostForm = context.forms.edit({
152
+ url: apiUrl(COST_URL, selectedRecord == null ? void 0 : selectedRecord.pk),
153
+ title: "Edit Manufacturing Cost",
154
+ fields: costFields,
155
+ successMessage: "Cost updated",
156
+ onFormSuccess: () => {
157
+ dataQuery.refetch();
158
+ }
159
+ });
160
+ const deleteCostForm = context.forms.delete({
161
+ url: apiUrl(COST_URL, selectedRecord == null ? void 0 : selectedRecord.pk),
162
+ title: "Delete Manufacturing Cost",
163
+ successMessage: "Cost deleted",
164
+ onFormSuccess: () => {
165
+ dataQuery.refetch();
166
+ }
167
+ });
168
+ const rowActions = useCallback(
169
+ (record) => {
170
+ const partPk = context.instance.pk;
171
+ return [
172
+ RowEditAction({
173
+ onClick: () => {
174
+ setSelectedRecord(record);
175
+ editCostForm == null ? void 0 : editCostForm.open();
176
+ },
177
+ hidden: record.part != partPk
178
+ }),
179
+ RowDuplicateAction({
180
+ onClick: () => {
181
+ setSelectedRecord(record);
182
+ duplicateCostForm == null ? void 0 : duplicateCostForm.open();
183
+ }
184
+ }),
185
+ RowDeleteAction({
186
+ onClick: () => {
187
+ setSelectedRecord(record);
188
+ deleteCostForm == null ? void 0 : deleteCostForm.open();
189
+ },
190
+ hidden: record.part != partPk
191
+ })
192
+ ];
193
+ },
194
+ [context.instance]
195
+ );
196
+ const tableColums = useMemo(() => {
197
+ return [
198
+ {
199
+ accessor: "part",
200
+ title: "Part",
201
+ render: (record) => {
202
+ var _a;
203
+ return (_a = record.part_detail) == null ? void 0 : _a.full_name;
204
+ }
205
+ },
206
+ {
207
+ accessor: "part_detail.IPN",
208
+ title: "IPN"
209
+ },
210
+ {
211
+ accessor: "description",
212
+ title: "Description"
213
+ },
214
+ {
215
+ accessor: "quantity",
216
+ title: "Quantity",
217
+ format: (value) => formatDecimal(value)
218
+ },
219
+ {
220
+ accessor: "rate",
221
+ title: "Rate",
222
+ render: (record) => {
223
+ const rate = record.rate_detail;
224
+ let unit_cost = "";
225
+ if (rate) {
226
+ unit_cost = formatCurrencyValue(rate.price, {
227
+ currency: rate.price_currency
228
+ });
229
+ } else {
230
+ unit_cost = formatCurrencyValue(record.unit_cost, {
231
+ currency: record.unit_cost_currency
232
+ });
233
+ }
234
+ return /* @__PURE__ */ React.createElement(Group, { justify: "space-between", gap: "sm" }, /* @__PURE__ */ React.createElement(Text, null, unit_cost), rate && /* @__PURE__ */ React.createElement(HoverCard, null, /* @__PURE__ */ React.createElement(HoverCard.Target, null, /* @__PURE__ */ React.createElement(ActionIcon, { variant: "transparent", size: "sm" }, /* @__PURE__ */ React.createElement(IconInfoCircle, null))), /* @__PURE__ */ React.createElement(HoverCard.Dropdown, null, /* @__PURE__ */ React.createElement(RenderRate, { instance: rate }))));
235
+ }
236
+ },
237
+ {
238
+ accessor: "notes",
239
+ title: "Notes"
240
+ },
241
+ {
242
+ accessor: "inherited",
243
+ title: "Inherited",
244
+ render: (record) => record.inherited ? "Yes" : "No"
245
+ },
246
+ {
247
+ accessor: "active",
248
+ title: "Active",
249
+ render: (record) => record.active ? "Yes" : "No"
250
+ },
251
+ {
252
+ accessor: "updated",
253
+ title: "Updated",
254
+ render: (record) => {
255
+ return /* @__PURE__ */ React.createElement(Group, { justify: "space-between" }, /* @__PURE__ */ React.createElement(Text, null, record.updated), record.updated_by_detail && /* @__PURE__ */ React.createElement(HoverCard, { position: "bottom-end" }, /* @__PURE__ */ React.createElement(HoverCard.Target, null, /* @__PURE__ */ React.createElement(ActionIcon, { variant: "transparent", size: "sm" }, /* @__PURE__ */ React.createElement(IconUser, null))), /* @__PURE__ */ React.createElement(HoverCard.Dropdown, null, context.renderInstance({
256
+ model: ModelType.user,
257
+ instance: record.updated_by_detail
258
+ }))));
259
+ }
260
+ },
261
+ {
262
+ accessor: "---",
263
+ title: " ",
264
+ width: 50,
265
+ resizable: false,
266
+ sortable: false,
267
+ render: (record, index) => /* @__PURE__ */ React.createElement(RowActions, { actions: rowActions(record), index })
268
+ }
269
+ ];
270
+ }, []);
271
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, createCostForm.modal, duplicateCostForm.modal, editCostForm.modal, deleteCostForm.modal, /* @__PURE__ */ React.createElement(Stack, { gap: "xs" }, /* @__PURE__ */ React.createElement(
272
+ Alert,
273
+ {
274
+ color: "blue",
275
+ icon: /* @__PURE__ */ React.createElement(IconInfoCircle, null),
276
+ title: "Manufacturing Costs"
277
+ },
278
+ "Additional manufacturing costs associated with this assembly."
279
+ ), dataQuery.isError && /* @__PURE__ */ React.createElement(
280
+ Alert,
281
+ {
282
+ color: "red",
283
+ title: "Error Fetching Data",
284
+ icon: /* @__PURE__ */ React.createElement(IconExclamationCircle, null)
285
+ },
286
+ dataQuery.error instanceof Error ? dataQuery.error.message : "An error occurred while fetching data from the server."
287
+ ), /* @__PURE__ */ React.createElement(Group, { justify: "space-between" }, /* @__PURE__ */ React.createElement(Group, { gap: "xs" }, /* @__PURE__ */ React.createElement(
288
+ AddItemButton,
289
+ {
290
+ tooltip: "Add new rate",
291
+ onClick: () => {
292
+ createCostForm.open();
293
+ }
294
+ }
295
+ )), /* @__PURE__ */ React.createElement(Group, { gap: "xs" }, /* @__PURE__ */ React.createElement(
296
+ SearchInput,
297
+ {
298
+ searchCallback: (value) => {
299
+ setSearchTerm(value);
300
+ }
301
+ }
302
+ ), /* @__PURE__ */ React.createElement(Tooltip, { label: "Refresh data", position: "top-end" }, /* @__PURE__ */ React.createElement(
303
+ ActionIcon,
304
+ {
305
+ variant: "transparent",
306
+ onClick: () => {
307
+ dataQuery.refetch();
308
+ }
309
+ },
310
+ /* @__PURE__ */ React.createElement(IconRefresh, null)
311
+ )), /* @__PURE__ */ React.createElement(Menu, null, /* @__PURE__ */ React.createElement(Menu.Target, null, /* @__PURE__ */ React.createElement(Button, { leftSection: /* @__PURE__ */ React.createElement(IconFileDownload, null) }, "Export")), /* @__PURE__ */ React.createElement(Menu.Dropdown, null, /* @__PURE__ */ React.createElement(Menu.Item, { onClick: () => downloadData("csv") }, "CSV"), /* @__PURE__ */ React.createElement(Menu.Item, { onClick: () => downloadData("xls") }, "XLS"), /* @__PURE__ */ React.createElement(Menu.Item, { onClick: () => downloadData("xlsx") }, "XLSX"))))), /* @__PURE__ */ React.createElement(
312
+ qr,
313
+ {
314
+ minHeight: 250,
315
+ withTableBorder: true,
316
+ withColumnBorders: true,
317
+ idAccessor: "pk",
318
+ noRecordsText: "No manufacturing costs found",
319
+ fetching: dataQuery.isFetching || dataQuery.isLoading,
320
+ columns: tableColums,
321
+ records: dataQuery.data || [],
322
+ pinLastColumn: true
323
+ }
324
+ )));
325
+ }
326
+ function renderPartPanel(context) {
327
+ checkPluginVersion(context);
328
+ return /* @__PURE__ */ React.createElement(ManufacturingCostsPanel, { context });
329
+ }
330
+ export {
331
+ renderPartPanel
332
+ };
333
+ //# sourceMappingURL=PartPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PartPanel.js","sources":["../../frontend/node_modules/@inventreedb/ui/dist/enums/ModelType.js","../../frontend/node_modules/@tabler/icons-react/dist/esm/icons/IconFileDownload.mjs","../../frontend/node_modules/@tabler/icons-react/dist/esm/icons/IconUser.mjs","../../frontend/src/PartPanel.tsx"],"sourcesContent":["var ModelType = /* @__PURE__ */ ((ModelType2) => {\n ModelType2[\"part\"] = \"part\";\n ModelType2[\"supplierpart\"] = \"supplierpart\";\n ModelType2[\"manufacturerpart\"] = \"manufacturerpart\";\n ModelType2[\"partcategory\"] = \"partcategory\";\n ModelType2[\"partparametertemplate\"] = \"partparametertemplate\";\n ModelType2[\"parttesttemplate\"] = \"parttesttemplate\";\n ModelType2[\"projectcode\"] = \"projectcode\";\n ModelType2[\"stockitem\"] = \"stockitem\";\n ModelType2[\"stocklocation\"] = \"stocklocation\";\n ModelType2[\"stocklocationtype\"] = \"stocklocationtype\";\n ModelType2[\"stockhistory\"] = \"stockhistory\";\n ModelType2[\"build\"] = \"build\";\n ModelType2[\"buildline\"] = \"buildline\";\n ModelType2[\"builditem\"] = \"builditem\";\n ModelType2[\"company\"] = \"company\";\n ModelType2[\"purchaseorder\"] = \"purchaseorder\";\n ModelType2[\"purchaseorderlineitem\"] = \"purchaseorderlineitem\";\n ModelType2[\"salesorder\"] = \"salesorder\";\n ModelType2[\"salesordershipment\"] = \"salesordershipment\";\n ModelType2[\"returnorder\"] = \"returnorder\";\n ModelType2[\"returnorderlineitem\"] = \"returnorderlineitem\";\n ModelType2[\"importsession\"] = \"importsession\";\n ModelType2[\"address\"] = \"address\";\n ModelType2[\"contact\"] = \"contact\";\n ModelType2[\"owner\"] = \"owner\";\n ModelType2[\"user\"] = \"user\";\n ModelType2[\"group\"] = \"group\";\n ModelType2[\"reporttemplate\"] = \"reporttemplate\";\n ModelType2[\"labeltemplate\"] = \"labeltemplate\";\n ModelType2[\"pluginconfig\"] = \"pluginconfig\";\n ModelType2[\"contenttype\"] = \"contenttype\";\n ModelType2[\"selectionlist\"] = \"selectionlist\";\n ModelType2[\"error\"] = \"error\";\n return ModelType2;\n})(ModelType || {});\nexport {\n ModelType\n};\n//# sourceMappingURL=ModelType.js.map\n","/**\n * @license @tabler/icons-react v3.34.1 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M14 3v4a1 1 0 0 0 1 1h4\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M12 17v-6\", \"key\": \"svg-2\" }], [\"path\", { \"d\": \"M9.5 14.5l2.5 2.5l2.5 -2.5\", \"key\": \"svg-3\" }]];\nconst IconFileDownload = createReactComponent(\"outline\", \"file-download\", \"FileDownload\", __iconNode);\n\nexport { __iconNode, IconFileDownload as default };\n//# sourceMappingURL=IconFileDownload.mjs.map\n","/**\n * @license @tabler/icons-react v3.34.1 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2\", \"key\": \"svg-1\" }]];\nconst IconUser = createReactComponent(\"outline\", \"user\", \"User\", __iconNode);\n\nexport { __iconNode, IconUser as default };\n//# sourceMappingURL=IconUser.mjs.map\n","// Import for type checking\nimport {\n AddItemButton,\n type ApiFormFieldSet,\n apiUrl,\n checkPluginVersion,\n formatCurrencyValue,\n formatDecimal,\n type InvenTreePluginContext,\n ModelType,\n RowActions,\n RowDeleteAction,\n RowDuplicateAction,\n RowEditAction,\n SearchInput\n} from '@inventreedb/ui';\nimport {\n ActionIcon,\n Alert,\n Button,\n Group,\n HoverCard,\n Menu,\n Stack,\n Text,\n Tooltip\n} from '@mantine/core';\nimport {\n IconExclamationCircle,\n IconFileDownload,\n IconInfoCircle,\n IconRefresh,\n IconUser\n} from '@tabler/icons-react';\nimport { useQuery } from '@tanstack/react-query';\nimport { DataTable } from 'mantine-datatable';\nimport { useCallback, useMemo, useState } from 'react';\n\nfunction RenderRate({ instance }: { instance: any }) {\n return (\n <Group gap='xs' justify='space-between'>\n <Text>{instance.name}</Text>\n <Group gap='xs' justify='right'>\n <Text size='xs'>{instance.description}</Text>\n {instance.units && <Text size='xs'>[{instance.units}]</Text>}\n </Group>\n </Group>\n );\n}\n\n/**\n * Render a custom panel with the provided context.\n * Refer to the InvenTree documentation for the context interface\n * https://docs.inventree.org/en/latest/plugins/mixins/ui/#plugin-context\n */\nfunction ManufacturingCostsPanel({\n context\n}: {\n context: InvenTreePluginContext;\n}) {\n const partId = useMemo(() => {\n return context.model == ModelType.part ? context.id || null : null;\n }, [context.model, context.id]);\n\n const [searchTerm, setSearchTerm] = useState<string>('');\n\n const RATE_URL: string = '/plugin/manufacturing-costs/rate/';\n const COST_URL: string = '/plugin/manufacturing-costs/cost/';\n const EXPORT_URL: string = '/plugin/manufacturing-costs/cost/export/';\n\n // Callback to download the manufacturing cost data\n const downloadData = useCallback(\n (exportFormat: string) => {\n if (!partId) {\n return;\n }\n\n let url = `${apiUrl(EXPORT_URL)}?part=${partId}&export_format=${exportFormat}`;\n\n if (context.host) {\n url = `${context.host}${url}`;\n } else {\n url = `${window.location.origin}${url}`;\n }\n\n // TODO: Support other export options, besides data format\n\n window.open(url, '_blank');\n },\n [partId, context.host, window.location]\n );\n\n const dataQuery = useQuery(\n {\n queryKey: ['manufacturing-cost', partId, searchTerm],\n queryFn: async () => {\n const url = `${COST_URL}`;\n\n return (\n context.api\n ?.get(url, {\n params: {\n part: partId,\n search: searchTerm\n }\n })\n .then((response) => response.data)\n .catch(() => []) ?? []\n );\n }\n },\n context.queryClient\n );\n\n // Record which is selected in the table\n const [selectedRecord, setSelectedRecord] = useState<any>(null);\n\n const costFields: ApiFormFieldSet = useMemo(() => {\n return {\n part: {\n value: partId,\n disabled: true\n },\n description: {},\n quantity: {},\n // TODO: Add a \"pre-field\" element here\n rate: {\n api_url: apiUrl(RATE_URL),\n modelRenderer: RenderRate\n },\n // TODO: Mark unit_cost and unit_cost currency as \"disabled\" if a rate is selected\n unit_cost: {},\n unit_cost_currency: {},\n notes: {},\n inherited: {},\n active: {}\n };\n }, []);\n\n const createCostForm = context.forms.create({\n url: apiUrl(COST_URL),\n title: 'Add Manufacturing Cost',\n fields: costFields,\n successMessage: 'Cost created',\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n const duplicateCostForm = context.forms.create({\n url: apiUrl(COST_URL),\n title: 'Add Manufacturing Cost',\n fields: costFields,\n successMessage: 'Cost created',\n initialData: selectedRecord,\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n const editCostForm = context.forms.edit({\n url: apiUrl(COST_URL, selectedRecord?.pk),\n title: 'Edit Manufacturing Cost',\n fields: costFields,\n successMessage: 'Cost updated',\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n const deleteCostForm = context.forms.delete({\n url: apiUrl(COST_URL, selectedRecord?.pk),\n title: 'Delete Manufacturing Cost',\n successMessage: 'Cost deleted',\n onFormSuccess: () => {\n dataQuery.refetch();\n }\n });\n\n // Render the actions available for a given row in the table\n const rowActions = useCallback(\n (record: any) => {\n const partPk = context.instance.pk;\n\n return [\n RowEditAction({\n onClick: () => {\n setSelectedRecord(record);\n editCostForm?.open();\n },\n hidden: record.part != partPk\n }),\n RowDuplicateAction({\n onClick: () => {\n setSelectedRecord(record);\n duplicateCostForm?.open();\n }\n }),\n RowDeleteAction({\n onClick: () => {\n setSelectedRecord(record);\n deleteCostForm?.open();\n },\n hidden: record.part != partPk\n })\n ];\n },\n [context.instance]\n );\n\n const tableColums: any[] = useMemo(() => {\n return [\n {\n accessor: 'part',\n title: 'Part',\n render: (record: any) => record.part_detail?.full_name\n },\n {\n accessor: 'part_detail.IPN',\n title: 'IPN'\n },\n {\n accessor: 'description',\n title: 'Description'\n },\n {\n accessor: 'quantity',\n title: 'Quantity',\n format: (value: any) => formatDecimal(value)\n },\n {\n accessor: 'rate',\n title: 'Rate',\n render: (record: any) => {\n const rate = record.rate_detail;\n\n let unit_cost: string | number | null = '';\n\n if (rate) {\n unit_cost = formatCurrencyValue(rate.price, {\n currency: rate.price_currency\n });\n } else {\n unit_cost = formatCurrencyValue(record.unit_cost, {\n currency: record.unit_cost_currency\n });\n }\n\n return (\n <Group justify='space-between' gap='sm'>\n <Text>{unit_cost}</Text>\n {rate && (\n <HoverCard>\n <HoverCard.Target>\n <ActionIcon variant='transparent' size='sm'>\n <IconInfoCircle />\n </ActionIcon>\n </HoverCard.Target>\n <HoverCard.Dropdown>\n <RenderRate instance={rate} />\n </HoverCard.Dropdown>\n </HoverCard>\n )}\n </Group>\n );\n }\n },\n {\n accessor: 'notes',\n title: 'Notes'\n },\n {\n accessor: 'inherited',\n title: 'Inherited',\n render: (record: any) => (record.inherited ? 'Yes' : 'No')\n },\n {\n accessor: 'active',\n title: 'Active',\n render: (record: any) => (record.active ? 'Yes' : 'No')\n },\n {\n accessor: 'updated',\n title: 'Updated',\n render: (record: any) => {\n return (\n <Group justify='space-between'>\n <Text>{record.updated}</Text>\n {record.updated_by_detail && (\n <HoverCard position='bottom-end'>\n <HoverCard.Target>\n <ActionIcon variant='transparent' size='sm'>\n <IconUser />\n </ActionIcon>\n </HoverCard.Target>\n <HoverCard.Dropdown>\n {context.renderInstance({\n model: ModelType.user,\n instance: record.updated_by_detail\n })}\n </HoverCard.Dropdown>\n </HoverCard>\n )}\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 {createCostForm.modal}\n {duplicateCostForm.modal}\n {editCostForm.modal}\n {deleteCostForm.modal}\n <Stack gap='xs'>\n <Alert\n color='blue'\n icon={<IconInfoCircle />}\n title={'Manufacturing Costs'}\n >\n Additional manufacturing costs associated with this assembly.\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 createCostForm.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 <Menu>\n <Menu.Target>\n <Button leftSection={<IconFileDownload />}>Export</Button>\n </Menu.Target>\n <Menu.Dropdown>\n <Menu.Item onClick={() => downloadData('csv')}>CSV</Menu.Item>\n <Menu.Item onClick={() => downloadData('xls')}>XLS</Menu.Item>\n <Menu.Item onClick={() => downloadData('xlsx')}>XLSX</Menu.Item>\n </Menu.Dropdown>\n </Menu>\n </Group>\n </Group>\n <DataTable\n minHeight={250}\n withTableBorder\n withColumnBorders\n idAccessor={'pk'}\n noRecordsText='No manufacturing costs found'\n fetching={dataQuery.isFetching || dataQuery.isLoading}\n columns={tableColums}\n records={dataQuery.data || []}\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\n// export function renderPartPanel(component: HTMLDivElement, context: InvenTreePluginContext) {\nexport function renderPartPanel(context: InvenTreePluginContext) {\n checkPluginVersion(context);\n return <ManufacturingCostsPanel context={context} />;\n}\n"],"names":["__iconNode","DataTable"],"mappings":"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","x_google_ignoreList":[0,1,2]}