diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 9c6c9e013b..916deb8e8c 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -9,15 +9,13 @@ on:
pull_request_target:
types: [ "labeled", "closed" ]
-permissions:
- contents: write
-
jobs:
backport:
name: Backport PR
runs-on: ubuntu-latest
permissions:
contents: write
+ pull-requests: write
if: |
github.event.pull_request.merged == true
&& contains(github.event.pull_request.labels.*.name, 'backport')
@@ -31,7 +29,6 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
auto_backport_label_prefix: backport-to-
- add_original_reviewers: true
- name: Info log
if: ${{ success() }}
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
index 1fccc6bbf8..42af7251c2 100644
--- a/.github/workflows/docker.yaml
+++ b/.github/workflows/docker.yaml
@@ -128,7 +128,7 @@ jobs:
uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # pin@v3.2.0
- name: Set up cosign
if: github.event_name != 'pull_request'
- uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # pin@v3.4.0
+ uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # pin@v3.5.0
- name: Check if Dockerhub login is required
id: docker_login
run: |
diff --git a/.github/workflows/qc_checks.yaml b/.github/workflows/qc_checks.yaml
index 69125f4572..693e0b7539 100644
--- a/.github/workflows/qc_checks.yaml
+++ b/.github/workflows/qc_checks.yaml
@@ -213,7 +213,7 @@ jobs:
echo "Version: $version"
mkdir export/${version}
mv schema.yml export/${version}/api.yaml
- - uses: stefanzweifel/git-auto-commit-action@8756aa072ef5b4a080af5dc8fef36c5d586e521d # v5.0.0
+ - uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1
with:
commit_message: "Update API schema for ${version}"
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index e3633a6f27..2205ed9285 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10
+ uses: github/codeql-action/upload-sarif@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
with:
sarif_file: results.sarif
diff --git a/Procfile b/Procfile
index 5ea6512d73..9b81d80a4d 100644
--- a/Procfile
+++ b/Procfile
@@ -1,7 +1,7 @@
# Web process: gunicorn
web: env/bin/gunicorn --chdir $APP_HOME/src/backend/InvenTree -c src/backend/InvenTree/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:$PORT
# Worker process: qcluster
-worker: env/bin/python src/backendInvenTree/manage.py qcluster
+worker: env/bin/python src/backend/InvenTree/manage.py qcluster
# Invoke commands
invoke: echo "" | echo "" && . env/bin/activate && invoke
# CLI: Provided for backwards compatibility
diff --git a/backportrc.json b/backportrc.json
deleted file mode 100644
index ab8d11dccc..0000000000
--- a/backportrc.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "repoOwner": "Oliver Walters",
- "repoName": "InvenTree",
- "targetBranchChoices": [],
- "branchLabelMapping": {
- "^backport-to-(.+)$": "$1"
- }
-}
diff --git a/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx b/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx
index 9b4e0504b3..b4b894457a 100644
--- a/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx
+++ b/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx
@@ -1,5 +1,11 @@
import { t } from '@lingui/macro';
-import { SegmentedControl, SimpleGrid, Stack } from '@mantine/core';
+import {
+ Group,
+ SegmentedControl,
+ SimpleGrid,
+ Stack,
+ Text
+} from '@mantine/core';
import { ReactNode, useMemo, useState } from 'react';
import {
Bar,
@@ -17,6 +23,7 @@ import {
import { CHART_COLORS } from '../../../components/charts/colors';
import { formatDecimal, formatPriceRange } from '../../../defaults/formatters';
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
+import { ModelType } from '../../../enums/ModelType';
import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState';
import { TableColumn } from '../../../tables/Column';
@@ -110,7 +117,17 @@ export default function BomPricingPanel({
title: t`Quantity`,
sortable: true,
switchable: false,
- render: (record: any) => formatDecimal(record.quantity)
+ render: (record: any) => {
+ let quantity = formatDecimal(record.quantity);
+ let units = record.sub_part_detail?.units;
+
+ return (
+
+ {quantity}
+ {units && [{units}]}
+
+ );
+ }
},
{
accessor: 'unit_price',
@@ -178,7 +195,9 @@ export default function BomPricingPanel({
sub_part_detail: true,
has_pricing: true
},
- enableSelection: false
+ enableSelection: false,
+ modelType: ModelType.part,
+ modelField: 'sub_part'
}}
/>
{bomPricingData.length > 0 ? (
diff --git a/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx b/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx
index 5a99136391..981e9aba68 100644
--- a/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx
+++ b/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx
@@ -14,6 +14,7 @@ import {
import { CHART_COLORS } from '../../../components/charts/colors';
import { formatCurrency } from '../../../defaults/formatters';
import { ApiEndpoints } from '../../../enums/ApiEndpoints';
+import { ModelType } from '../../../enums/ModelType';
import { useTable } from '../../../hooks/UseTable';
import { apiUrl } from '../../../states/ApiState';
import { TableColumn } from '../../../tables/Column';
@@ -37,7 +38,7 @@ export default function VariantPricingPanel({
title: t`Variant Part`,
sortable: true,
switchable: false,
- render: (record: any) => PartColumn(record)
+ render: (record: any) => PartColumn(record, true)
},
{
accessor: 'pricing_min',
@@ -90,7 +91,8 @@ export default function VariantPricingPanel({
ancestor: part?.pk,
has_pricing: true
},
- enablePagination: false
+ enablePagination: true,
+ modelType: ModelType.part
}}
/>
{variantPricingData.length > 0 ? (
diff --git a/src/frontend/src/tables/ColumnRenderers.tsx b/src/frontend/src/tables/ColumnRenderers.tsx
index 5353a467b8..f1d39858cf 100644
--- a/src/frontend/src/tables/ColumnRenderers.tsx
+++ b/src/frontend/src/tables/ColumnRenderers.tsx
@@ -16,8 +16,13 @@ import { TableColumn } from './Column';
import { ProjectCodeHoverCard } from './TableHoverCard';
// Render a Part instance within a table
-export function PartColumn(part: any) {
- return ;
+export function PartColumn(part: any, full_name?: boolean) {
+ return (
+
+ );
}
export function BooleanColumn({
diff --git a/src/frontend/src/tables/bom/BomTable.tsx b/src/frontend/src/tables/bom/BomTable.tsx
index 6abf1425d5..8f6aafadec 100644
--- a/src/frontend/src/tables/bom/BomTable.tsx
+++ b/src/frontend/src/tables/bom/BomTable.tsx
@@ -1,21 +1,26 @@
import { t } from '@lingui/macro';
-import { Text } from '@mantine/core';
+import { Group, Text } from '@mantine/core';
import {
IconArrowRight,
IconCircleCheck,
IconSwitch3
} from '@tabler/icons-react';
-import { ReactNode, useCallback, useMemo } from 'react';
+import { ReactNode, useCallback, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
+import { AddItemButton } from '../../components/buttons/AddItemButton';
import { YesNoButton } from '../../components/buttons/YesNoButton';
import { Thumbnail } from '../../components/images/Thumbnail';
-import { formatPriceRange } from '../../defaults/formatters';
+import { formatDecimal, formatPriceRange } from '../../defaults/formatters';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { UserRoles } from '../../enums/Roles';
import { bomItemFields } from '../../forms/BomForms';
-import { openDeleteApiForm, openEditApiForm } from '../../functions/forms';
+import {
+ useCreateApiFormModal,
+ useDeleteApiFormModal,
+ useEditApiFormModal
+} from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable';
import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState';
@@ -98,9 +103,19 @@ export function BomTable({
{
accessor: 'quantity',
switchable: false,
- sortable: true
- // TODO: Custom quantity renderer
- // TODO: see bom.js for existing implementation
+ sortable: true,
+ render: (record: any) => {
+ let quantity = formatDecimal(record.quantity);
+ let units = record.sub_part_detail?.units;
+
+ return (
+
+ {quantity}
+ {record.overage && +{record.overage}}
+ {units && {units}}
+
+ );
+ }
},
{
accessor: 'substitutes',
@@ -131,12 +146,22 @@ export function BomTable({
}),
{
accessor: 'price_range',
- title: t`Price Range`,
-
- sortable: false,
+ title: t`Unit Price`,
+ ordering: 'pricing_max',
+ sortable: true,
+ switchable: true,
render: (record: any) =>
formatPriceRange(record.pricing_min, record.pricing_max)
},
+ {
+ accessor: 'total_price',
+ title: t`Total Price`,
+ ordering: 'pricing_max_total',
+ sortable: true,
+ switchable: true,
+ render: (record: any) =>
+ formatPriceRange(record.pricing_min_total, record.pricing_max_total)
+ },
{
accessor: 'available_stock',
sortable: true,
@@ -277,6 +302,36 @@ export function BomTable({
];
}, [partId, params]);
+ const [selectedBomItem, setSelectedBomItem] = useState(0);
+
+ const newBomItem = useCreateApiFormModal({
+ url: ApiEndpoints.bom_list,
+ title: t`Create BOM Item`,
+ fields: bomItemFields(),
+ initialData: {
+ part: partId
+ },
+ successMessage: t`BOM item created`,
+ onFormSuccess: table.refreshTable
+ });
+
+ const editBomItem = useEditApiFormModal({
+ url: ApiEndpoints.bom_list,
+ pk: selectedBomItem,
+ title: t`Edit BOM Item`,
+ fields: bomItemFields(),
+ successMessage: t`BOM item updated`,
+ onFormSuccess: table.refreshTable
+ });
+
+ const deleteBomItem = useDeleteApiFormModal({
+ url: ApiEndpoints.bom_list,
+ pk: selectedBomItem,
+ title: t`Delete BOM Item`,
+ successMessage: t`BOM item deleted`,
+ onFormSuccess: table.refreshTable
+ });
+
const rowActions = useCallback(
(record: any) => {
// If this BOM item is defined for a *different* parent, then it cannot be edited
@@ -313,14 +368,8 @@ export function BomTable({
RowEditAction({
hidden: !user.hasChangeRole(UserRoles.part),
onClick: () => {
- openEditApiForm({
- url: ApiEndpoints.bom_list,
- pk: record.pk,
- title: t`Edit Bom Item`,
- fields: bomItemFields(),
- successMessage: t`Bom item updated`,
- onFormSuccess: table.refreshTable
- });
+ setSelectedBomItem(record.pk);
+ editBomItem.open();
}
})
);
@@ -330,14 +379,8 @@ export function BomTable({
RowDeleteAction({
hidden: !user.hasDeleteRole(UserRoles.part),
onClick: () => {
- openDeleteApiForm({
- url: ApiEndpoints.bom_list,
- pk: record.pk,
- title: t`Delete Bom Item`,
- successMessage: t`Bom item deleted`,
- onFormSuccess: table.refreshTable,
- preFormWarning: t`Are you sure you want to remove this BOM item?`
- });
+ setSelectedBomItem(record.pk);
+ deleteBomItem.open();
}
})
);
@@ -347,22 +390,38 @@ export function BomTable({
[partId, user]
);
+ const tableActions = useMemo(() => {
+ return [
+ newBomItem.open()}
+ />
+ ];
+ }, [user]);
+
return (
-
+ <>
+ {newBomItem.modal}
+ {editBomItem.modal}
+ {deleteBomItem.modal}
+
+ >
);
}
diff --git a/src/frontend/src/tables/bom/UsedInTable.tsx b/src/frontend/src/tables/bom/UsedInTable.tsx
index 3bb1911ce4..dd8aaa5a0b 100644
--- a/src/frontend/src/tables/bom/UsedInTable.tsx
+++ b/src/frontend/src/tables/bom/UsedInTable.tsx
@@ -1,7 +1,9 @@
import { t } from '@lingui/macro';
+import { Group, Text } from '@mantine/core';
import { useMemo } from 'react';
import { PartHoverCard } from '../../components/images/Thumbnail';
+import { formatDecimal } from '../../defaults/formatters';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { useTable } from '../../hooks/UseTable';
@@ -39,8 +41,15 @@ export function UsedInTable({
{
accessor: 'quantity',
render: (record: any) => {
- // TODO: render units if appropriate
- return record.quantity;
+ let quantity = formatDecimal(record.quantity);
+ let units = record.sub_part_detail?.units;
+
+ return (
+
+ {quantity}
+ {units && {units}}
+
+ );
}
},
ReferenceColumn()
diff --git a/src/frontend/src/tables/part/PartTable.tsx b/src/frontend/src/tables/part/PartTable.tsx
index 7c43414dff..14adb99e45 100644
--- a/src/frontend/src/tables/part/PartTable.tsx
+++ b/src/frontend/src/tables/part/PartTable.tsx
@@ -158,7 +158,8 @@ function partTableColumns(): TableColumn[] {
{
accessor: 'price_range',
title: t`Price Range`,
- sortable: false,
+ sortable: true,
+ ordering: 'pricing_max',
render: (record: any) =>
formatPriceRange(record.pricing_min, record.pricing_max)
},