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 [ +