From 3303e4664fb409ab35162cf2d44feda3920b4514 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 2 Jul 2026 12:38:07 +0000 Subject: [PATCH] Implement parameter duplication for more models: - Company - ManufacturerPart - SupplierPart --- src/frontend/src/forms/CompanyForms.tsx | 75 ++++++++++++++++--- .../src/pages/company/CompanyDetail.tsx | 21 +++++- .../pages/company/ManufacturerPartDetail.tsx | 6 +- .../src/pages/company/SupplierPartDetail.tsx | 6 +- .../purchasing/ManufacturerPartTable.tsx | 6 +- .../tables/purchasing/SupplierPartTable.tsx | 6 +- 6 files changed, 106 insertions(+), 14 deletions(-) diff --git a/src/frontend/src/forms/CompanyForms.tsx b/src/frontend/src/forms/CompanyForms.tsx index 14d3b6218f..2253d92305 100644 --- a/src/frontend/src/forms/CompanyForms.tsx +++ b/src/frontend/src/forms/CompanyForms.tsx @@ -21,11 +21,13 @@ import { TagsField } from './CommonFields'; export function useSupplierPartFields({ manufacturerId, manufacturerPartId, - partId + partId, + duplicateSupplierPartId }: { manufacturerId?: number; manufacturerPartId?: number; partId?: number; + duplicateSupplierPartId?: number | null; }) { const [part, setPart] = useState({}); @@ -95,14 +97,37 @@ export function useSupplierPartFields({ icon: }, primary: {}, - active: {} + active: {}, + duplicate: { + children: { + original: { + value: duplicateSupplierPartId, + hidden: true + }, + copy_parameters: {} + } + } }; + if (!duplicateSupplierPartId) { + delete fields.duplicate; + } + return fields; - }, [manufacturerId, manufacturerPartId, partId, part]); + }, [ + manufacturerId, + manufacturerPartId, + partId, + part, + duplicateSupplierPartId + ]); } -export function useManufacturerPartFields() { +export function useManufacturerPartFields({ + duplicateManufacturerPartId +}: { + duplicateManufacturerPartId?: number | null; +} = {}) { return useMemo(() => { const fields: ApiFormFieldSet = { part: {}, @@ -120,18 +145,35 @@ export function useManufacturerPartFields() { MPN: {}, description: {}, tags: TagsField({}), - link: {} + link: {}, + duplicate: { + children: { + original: { + value: duplicateManufacturerPartId, + hidden: true + }, + copy_parameters: {} + } + } }; + if (!duplicateManufacturerPartId) { + delete fields.duplicate; + } + return fields; - }, []); + }, [duplicateManufacturerPartId]); } /** * Field set for editing a company instance */ -export function companyFields(): ApiFormFieldSet { - return { +export function companyFields({ + duplicateCompanyId +}: { + duplicateCompanyId?: number | null; +} = {}): ApiFormFieldSet { + const fields: ApiFormFieldSet = { name: {}, description: {}, website: { @@ -151,6 +193,21 @@ export function companyFields(): ApiFormFieldSet { is_supplier: {}, is_manufacturer: {}, is_customer: {}, - active: {} + active: {}, + duplicate: { + children: { + original: { + value: duplicateCompanyId, + hidden: true + }, + copy_parameters: {} + } + } }; + + if (!duplicateCompanyId) { + delete fields.duplicate; + } + + return fields; } diff --git a/src/frontend/src/pages/company/CompanyDetail.tsx b/src/frontend/src/pages/company/CompanyDetail.tsx index 2b779d4112..5765cc377f 100644 --- a/src/frontend/src/pages/company/CompanyDetail.tsx +++ b/src/frontend/src/pages/company/CompanyDetail.tsx @@ -31,6 +31,7 @@ import { DetailsImage } from '../../components/details/DetailsImage'; import { ItemDetailsGrid } from '../../components/details/ItemDetails'; import { DeleteItemAction, + DuplicateItemAction, EditItemAction, OptionsActionDropdown } from '../../components/items/ActionDropdown'; @@ -43,6 +44,7 @@ import { PanelGroup } from '../../components/panels/PanelGroup'; import ParametersPanel from '../../components/panels/ParametersPanel'; import { companyFields } from '../../forms/CompanyForms'; import { + useCreateApiFormModal, useDeleteApiFormModal, useEditApiFormModal } from '../../hooks/UseForm'; @@ -293,11 +295,23 @@ export default function CompanyDetail(props: Readonly) { url: ApiEndpoints.company_list, pk: company?.pk, title: t`Edit Company`, - fields: companyFields(), + fields: useMemo(() => companyFields({}), []), queryParams: new URLSearchParams({ tags: 'true' }), onFormSuccess: refreshInstance }); + const duplicateCompany = useCreateApiFormModal({ + url: ApiEndpoints.company_list, + title: t`Duplicate Company`, + initialData: useMemo(() => ({ ...company }), [company]), + fields: useMemo( + () => companyFields({ duplicateCompanyId: company?.pk }), + [company] + ), + follow: true, + modelType: ModelType.company + }); + const deleteCompany = useDeleteApiFormModal({ url: ApiEndpoints.company_list, pk: company?.pk, @@ -322,6 +336,10 @@ export default function CompanyDetail(props: Readonly) { hidden: !user.hasChangeRole(UserRoles.purchase_order), onClick: () => editCompany.open() }), + DuplicateItemAction({ + hidden: !user.hasAddRole(UserRoles.purchase_order), + onClick: () => duplicateCompany.open() + }), DeleteItemAction({ hidden: !user.hasDeleteRole(UserRoles.purchase_order), onClick: () => deleteCompany.open() @@ -345,6 +363,7 @@ export default function CompanyDetail(props: Readonly) { <> {editCompany.modal} {deleteCompany.modal} + {duplicateCompany.modal} manufacturerPartFields, [manufacturerPartFields]), + fields: duplicateManufacturerPartFields, table: table, initialData: { ...selectedPart diff --git a/src/frontend/src/tables/purchasing/SupplierPartTable.tsx b/src/frontend/src/tables/purchasing/SupplierPartTable.tsx index 4e38368bce..b6e087fbf4 100644 --- a/src/frontend/src/tables/purchasing/SupplierPartTable.tsx +++ b/src/frontend/src/tables/purchasing/SupplierPartTable.tsx @@ -292,10 +292,14 @@ export function SupplierPartTable({ } }); + const duplicateSupplierPartFields = useSupplierPartFields({ + duplicateSupplierPartId: selectedSupplierPart?.pk + }); + const duplicateSupplierPart = useCreateApiFormModal({ url: ApiEndpoints.supplier_part_list, title: t`Add Supplier Part`, - fields: useMemo(() => editSupplierPartFields, [editSupplierPartFields]), + fields: duplicateSupplierPartFields, initialData: { ...selectedSupplierPart, primary: false,