diff --git a/CHANGELOG.md b/CHANGELOG.md index 410d9a6054..4bea76d17c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- [#12250](https://github.com/inventree/InvenTree/pull/12250) adds "active" field to the ProjectCode model and API endpoints + ### Changed ### Removed diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 1bd9877491..b34c1ef5c5 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 512 +INVENTREE_API_VERSION = 513 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v513 -> 2026-06-25 : https://github.com/inventree/InvenTree/pull/12250 + - Adds "active" field to the ProjectCode model and API endpoints + v512 -> 2026-06-20 : https://github.com/inventree/InvenTree/pull/12022 - Adds optional "merge" field to each item in the Stock Transfer API endpoint - When merge is enabled, transferred stock is combined into compatible existing stock at the destination diff --git a/src/backend/InvenTree/common/admin.py b/src/backend/InvenTree/common/admin.py index e35d269f0b..44ac152760 100644 --- a/src/backend/InvenTree/common/admin.py +++ b/src/backend/InvenTree/common/admin.py @@ -115,7 +115,8 @@ class BarcodeScanResultAdmin(admin.ModelAdmin): class ProjectCodeAdmin(admin.ModelAdmin): """Admin settings for ProjectCode.""" - list_display = ('code', 'description') + list_display = ('code', 'description', 'active') + list_filter = ('active',) search_fields = ('code', 'description') diff --git a/src/backend/InvenTree/common/api.py b/src/backend/InvenTree/common/api.py index f2bec5e94b..f2a1a4c636 100644 --- a/src/backend/InvenTree/common/api.py +++ b/src/backend/InvenTree/common/api.py @@ -491,7 +491,7 @@ class ProjectCodeList(DataExportViewMixin, ListCreateAPI): filter_backends = SEARCH_ORDER_FILTER ordering_fields = ['code'] - + filterset_fields = ['active'] search_fields = ['code', 'description'] diff --git a/src/backend/InvenTree/common/migrations/0045_projectcode_active.py b/src/backend/InvenTree/common/migrations/0045_projectcode_active.py new file mode 100644 index 0000000000..c0a8461972 --- /dev/null +++ b/src/backend/InvenTree/common/migrations/0045_projectcode_active.py @@ -0,0 +1,22 @@ +# Generated by Django 5.2.15 on 2026-06-25 03:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("common", "0044_notificationmessage_charfield_pk"), + ] + + operations = [ + migrations.AddField( + model_name="projectcode", + name="active", + field=models.BooleanField( + default=True, + help_text="Is this project code active?", + verbose_name="Active", + ), + ), + ] diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index 3e903ac9d2..b07dd08eeb 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -181,6 +181,12 @@ class ProjectCode(InvenTree.models.InvenTreeMetadataModel): help_text=_('Project description'), ) + active = models.BooleanField( + default=True, + verbose_name=_('Active'), + help_text=_('Is this project code active?'), + ) + responsible = models.ForeignKey( users.models.Owner, on_delete=models.SET_NULL, diff --git a/src/backend/InvenTree/common/serializers.py b/src/backend/InvenTree/common/serializers.py index 3c0ef9e859..f37e46e594 100644 --- a/src/backend/InvenTree/common/serializers.py +++ b/src/backend/InvenTree/common/serializers.py @@ -417,7 +417,14 @@ class ProjectCodeSerializer(DataImportExportSerializerMixin, InvenTreeModelSeria """Meta options for ProjectCodeSerializer.""" model = common_models.ProjectCode - fields = ['pk', 'code', 'description', 'responsible', 'responsible_detail'] + fields = [ + 'pk', + 'code', + 'description', + 'active', + 'responsible', + 'responsible_detail', + ] responsible_detail = OwnerSerializer( source='responsible', read_only=True, allow_null=True diff --git a/src/backend/InvenTree/common/tests.py b/src/backend/InvenTree/common/tests.py index 2bd0c41db8..6935e3b475 100644 --- a/src/backend/InvenTree/common/tests.py +++ b/src/backend/InvenTree/common/tests.py @@ -1752,6 +1752,22 @@ class ProjectCodesTest(InvenTreeAPITestCase): str(response.data['code']), ) + def test_filter_active(self): + """Test that the 'active' field can be filtered via the API.""" + # Mark one code as inactive + code = ProjectCode.objects.first() + code.active = False + code.save() + + active_count = ProjectCode.objects.filter(active=True).count() + inactive_count = ProjectCode.objects.filter(active=False).count() + + response = self.get(self.url, data={'active': True}, expected_code=200) + self.assertEqual(len(response.data), active_count) + + response = self.get(self.url, data={'active': False}, expected_code=200) + self.assertEqual(len(response.data), inactive_count) + def test_write_access(self): """Test that non-staff users have read-only access.""" # By default user has staff access, can create a new project code diff --git a/src/frontend/src/forms/BuildForms.tsx b/src/frontend/src/forms/BuildForms.tsx index 3fd3acbc41..2ff3c0bd53 100644 --- a/src/frontend/src/forms/BuildForms.tsx +++ b/src/frontend/src/forms/BuildForms.tsx @@ -5,7 +5,6 @@ import { IconCircleCheck, IconInfoCircle, IconLink, - IconList, IconSitemap, IconTruckDelivery, IconUsersGroup @@ -36,7 +35,7 @@ import { } from '../hooks/UseGenerator'; import { useGlobalSettingsState } from '../states/SettingsStates'; import { RenderPartColumn } from '../tables/ColumnRenderers'; -import { TagsField } from './CommonFields'; +import { ProjectCodeField, TagsField } from './CommonFields'; /** * Field set for BuildOrder forms @@ -93,9 +92,7 @@ export function useBuildOrderFields({ }, title: {}, quantity: {}, - project_code: { - icon: - }, + project_code: ProjectCodeField(), priority: {}, parent: { icon: , diff --git a/src/frontend/src/forms/CommonFields.tsx b/src/frontend/src/forms/CommonFields.tsx index 9fcd5fd25b..07f16e0bb9 100644 --- a/src/frontend/src/forms/CommonFields.tsx +++ b/src/frontend/src/forms/CommonFields.tsx @@ -1,5 +1,6 @@ import type { ApiFormFieldType } from '@lib/types/Forms'; import { t } from '@lingui/core/macro'; +import { IconList } from '@tabler/icons-react'; export function TagsField({ label, @@ -17,3 +18,14 @@ export function TagsField({ placeholder: placeholder ?? t`Select tags` }; } + +export function ProjectCodeField(): ApiFormFieldType { + return { + filters: { + active: true + }, + label: t`Project Code`, + description: t`Select project code for this item`, + icon: + }; +} diff --git a/src/frontend/src/forms/CommonForms.tsx b/src/frontend/src/forms/CommonForms.tsx index fc3ad49135..3bd64bc0d0 100644 --- a/src/frontend/src/forms/CommonForms.tsx +++ b/src/frontend/src/forms/CommonForms.tsx @@ -5,7 +5,6 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ModelType } from '@lib/enums/ModelType'; import { apiUrl } from '@lib/functions/Api'; import type { ApiFormFieldSet, ApiFormFieldType } from '@lib/types/Forms'; -import { t } from '@lingui/core/macro'; import type { StatusCodeInterface, StatusCodeListInterface @@ -13,6 +12,7 @@ import type { import { useApi } from '../contexts/ApiContext'; import { useGlobalStatusState } from '../states/GlobalStatusState'; import { useUserState } from '../states/UserState'; +import { ProjectCodeField } from './CommonFields'; export function projectCodeFields(): ApiFormFieldSet { return { @@ -20,7 +20,8 @@ export function projectCodeFields(): ApiFormFieldSet { description: {}, responsible: { icon: - } + }, + active: {} }; } @@ -90,9 +91,7 @@ export function extraLineItemFields(): ApiFormFieldSet { quantity: {}, price: {}, price_currency: {}, - project_code: { - description: t`Select project code for this line item` - }, + project_code: ProjectCodeField(), notes: {}, link: {} }; diff --git a/src/frontend/src/forms/PurchaseOrderForms.tsx b/src/frontend/src/forms/PurchaseOrderForms.tsx index 4faf5756ad..3dce873eb6 100644 --- a/src/frontend/src/forms/PurchaseOrderForms.tsx +++ b/src/frontend/src/forms/PurchaseOrderForms.tsx @@ -20,7 +20,6 @@ import { IconHash, IconInfoCircle, IconLink, - IconList, IconNotes, IconSitemap, IconUser, @@ -57,7 +56,7 @@ import { useSerialNumberGenerator } from '../hooks/UseGenerator'; import { useGlobalSettingsState } from '../states/SettingsStates'; -import { TagsField } from './CommonFields'; +import { ProjectCodeField, TagsField } from './CommonFields'; /* * Construct a set of fields for creating / editing a PurchaseOrderLineItem instance */ @@ -191,9 +190,7 @@ export function usePurchaseOrderLineItemFields({ value: autoPricing, onValueChange: setAutoPricing }, - project_code: { - description: t`Select project code for this line item` - }, + project_code: ProjectCodeField(), target_date: { icon: }, @@ -271,9 +268,7 @@ export function usePurchaseOrderFields({ } }, supplier_reference: {}, - project_code: { - icon: - }, + project_code: ProjectCodeField(), order_currency: { icon: }, diff --git a/src/frontend/src/forms/ReturnOrderForms.tsx b/src/frontend/src/forms/ReturnOrderForms.tsx index ea919f5da7..3377ff1f22 100644 --- a/src/frontend/src/forms/ReturnOrderForms.tsx +++ b/src/frontend/src/forms/ReturnOrderForms.tsx @@ -23,7 +23,7 @@ import { Thumbnail } from '../components/images/Thumbnail'; import { useCreateApiFormModal } from '../hooks/UseForm'; import { useGlobalSettingsState } from '../states/SettingsStates'; import { StatusFilterOptions } from '../tables/Filter'; -import { TagsField } from './CommonFields'; +import { ProjectCodeField, TagsField } from './CommonFields'; export function useReturnOrderFields({ duplicateOrderId @@ -44,7 +44,7 @@ export function useReturnOrderFields({ } }, customer_reference: {}, - project_code: {}, + project_code: ProjectCodeField(), order_currency: {}, start_date: { icon: @@ -138,9 +138,7 @@ export function useReturnOrderLineItemFields({ }, price: {}, price_currency: {}, - project_code: { - description: t`Select project code for this line item` - }, + project_code: ProjectCodeField(), target_date: {}, notes: {}, link: {} diff --git a/src/frontend/src/forms/SalesOrderForms.tsx b/src/frontend/src/forms/SalesOrderForms.tsx index 125ca7f894..a863a51852 100644 --- a/src/frontend/src/forms/SalesOrderForms.tsx +++ b/src/frontend/src/forms/SalesOrderForms.tsx @@ -31,7 +31,7 @@ import { useCreateApiFormModal, useEditApiFormModal } from '../hooks/UseForm'; import { useGlobalSettingsState } from '../states/SettingsStates'; import { useUserState } from '../states/UserState'; import { RenderPartColumn } from '../tables/ColumnRenderers'; -import { TagsField } from './CommonFields'; +import { ProjectCodeField, TagsField } from './CommonFields'; export function useSalesOrderFields({ duplicateOrderId @@ -57,7 +57,7 @@ export function useSalesOrderFields({ } }, customer_reference: {}, - project_code: {}, + project_code: ProjectCodeField(), order_currency: {}, start_date: { icon: @@ -194,9 +194,7 @@ export function useSalesOrderLineItemFields({ value: partCurrency, onValueChange: setPartCurrency }, - project_code: { - description: t`Select project code for this line item` - }, + project_code: ProjectCodeField(), target_date: {}, notes: {}, link: {} diff --git a/src/frontend/src/forms/TransferOrderForms.tsx b/src/frontend/src/forms/TransferOrderForms.tsx index 3935d89b92..638028603d 100644 --- a/src/frontend/src/forms/TransferOrderForms.tsx +++ b/src/frontend/src/forms/TransferOrderForms.tsx @@ -10,7 +10,7 @@ import type { TableFieldRowProps } from '../components/forms/fields/TableField'; import { useCreateApiFormModal } from '../hooks/UseForm'; import { useGlobalSettingsState } from '../states/SettingsStates'; import { RenderPartColumn } from '../tables/ColumnRenderers'; -import { TagsField } from './CommonFields'; +import { ProjectCodeField, TagsField } from './CommonFields'; export function useTransferOrderFields({ duplicateOrderId @@ -23,7 +23,7 @@ export function useTransferOrderFields({ const fields: ApiFormFieldSet = { reference: {}, description: {}, - project_code: {}, + project_code: ProjectCodeField(), start_date: { icon: }, @@ -91,9 +91,7 @@ export function useTransferOrderLineItemFields({ }, reference: {}, quantity: {}, - project_code: { - description: t`Select project code for this line item` - }, + project_code: ProjectCodeField(), target_date: {}, notes: {}, link: {} diff --git a/src/frontend/src/tables/settings/ProjectCodeTable.tsx b/src/frontend/src/tables/settings/ProjectCodeTable.tsx index d6cdddf9e8..e20e3210ed 100644 --- a/src/frontend/src/tables/settings/ProjectCodeTable.tsx +++ b/src/frontend/src/tables/settings/ProjectCodeTable.tsx @@ -11,6 +11,7 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import useTable from '@lib/hooks/UseTable'; +import type { TableFilter } from '@lib/index'; import type { TableColumn } from '@lib/types/Tables'; import { projectCodeFields } from '../../forms/CommonForms'; import { @@ -19,7 +20,11 @@ import { useEditApiFormModal } from '../../hooks/UseForm'; import { useUserState } from '../../states/UserState'; -import { DescriptionColumn, ResponsibleColumn } from '../ColumnRenderers'; +import { + BooleanColumn, + DescriptionColumn, + ResponsibleColumn +} from '../ColumnRenderers'; import { InvenTreeTable } from '../InvenTreeTable'; /** @@ -37,6 +42,9 @@ export default function ProjectCodeTable() { sortable: true }, DescriptionColumn({}), + BooleanColumn({ + accessor: 'active' + }), ResponsibleColumn({}) ]; }, []); @@ -89,6 +97,17 @@ export default function ProjectCodeTable() { [user] ); + const tableFilters: TableFilter[] = useMemo(() => { + return [ + { + name: 'active', + label: t`Active`, + description: t`Show active items`, + type: 'boolean' + } + ]; + }, []); + const tableActions = useMemo(() => { return [