mirror of
https://github.com/inventree/InvenTree.git
synced 2025-08-07 12:22:11 +00:00
Merge branch 'master' of https://github.com/inventree/InvenTree into pui-maintine-v7
This commit is contained in:
5
.github/workflows/backport.yml
vendored
5
.github/workflows/backport.yml
vendored
@@ -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() }}
|
||||
|
2
.github/workflows/docker.yaml
vendored
2
.github/workflows/docker.yaml
vendored
@@ -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: |
|
||||
|
2
.github/workflows/qc_checks.yaml
vendored
2
.github/workflows/qc_checks.yaml
vendored
@@ -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}"
|
||||
|
||||
|
2
.github/workflows/scorecard.yml
vendored
2
.github/workflows/scorecard.yml
vendored
@@ -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
|
||||
|
2
Procfile
2
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
|
||||
|
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"repoOwner": "Oliver Walters",
|
||||
"repoName": "InvenTree",
|
||||
"targetBranchChoices": [],
|
||||
"branchLabelMapping": {
|
||||
"^backport-to-(.+)$": "$1"
|
||||
}
|
||||
}
|
@@ -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 (
|
||||
<Group spacing="apart" grow>
|
||||
<Text>{quantity}</Text>
|
||||
{units && <Text size="xs">[{units}]</Text>}
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
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 ? (
|
||||
|
@@ -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 ? (
|
||||
|
@@ -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 <Thumbnail src={part?.thumbnail ?? part.image} text={part.name} />;
|
||||
export function PartColumn(part: any, full_name?: boolean) {
|
||||
return (
|
||||
<Thumbnail
|
||||
src={part?.thumbnail ?? part.image}
|
||||
text={full_name ? part.full_name : part.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function BooleanColumn({
|
||||
|
@@ -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 (
|
||||
<Group position="apart" grow>
|
||||
<Text>{quantity}</Text>
|
||||
{record.overage && <Text size="xs">+{record.overage}</Text>}
|
||||
{units && <Text size="xs">{units}</Text>}
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
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<number>(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,7 +390,21 @@ export function BomTable({
|
||||
[partId, user]
|
||||
);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
hidden={!user.hasAddRole(UserRoles.part)}
|
||||
tooltip={t`Add BOM Item`}
|
||||
onClick={() => newBomItem.open()}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{newBomItem.modal}
|
||||
{editBomItem.modal}
|
||||
{deleteBomItem.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.bom_list)}
|
||||
tableState={table}
|
||||
@@ -359,10 +416,12 @@ export function BomTable({
|
||||
part_detail: true,
|
||||
sub_part_detail: true
|
||||
},
|
||||
tableActions: tableActions,
|
||||
tableFilters: tableFilters,
|
||||
modelType: ModelType.part,
|
||||
rowActions: rowActions
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@@ -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 (
|
||||
<Group position="apart" grow>
|
||||
<Text>{quantity}</Text>
|
||||
{units && <Text size="xs">{units}</Text>}
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
},
|
||||
ReferenceColumn()
|
||||
|
@@ -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)
|
||||
},
|
||||
|
Reference in New Issue
Block a user