From ba8cd44a0ed2c613a5f2cee06bfc9bd0f61f21c7 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 7 Nov 2025 08:39:04 +0000 Subject: (최겸) 입찰/견적 수정사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/general-contract-items-table.tsx | 292 +++++++++++++++++++-- 1 file changed, 263 insertions(+), 29 deletions(-) (limited to 'lib/general-contracts/detail/general-contract-items-table.tsx') diff --git a/lib/general-contracts/detail/general-contract-items-table.tsx b/lib/general-contracts/detail/general-contract-items-table.tsx index 1b9a1a06..ed1e5afb 100644 --- a/lib/general-contracts/detail/general-contract-items-table.tsx +++ b/lib/general-contracts/detail/general-contract-items-table.tsx @@ -20,15 +20,26 @@ import { Package, Plus, Trash2, + FileSpreadsheet, + Save, + LoaderIcon } from 'lucide-react' import { toast } from 'sonner' import { updateContractItems, getContractItems } from '../service' -import { Save, LoaderIcon } from 'lucide-react' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' +import { ProjectSelector } from '@/components/ProjectSelector' +import { MaterialGroupSelectorDialogSingle } from '@/components/common/material/material-group-selector-dialog-single' +import { MaterialSearchItem } from '@/lib/material/material-group-service' interface ContractItem { id?: number + projectId?: number | null + projectName?: string + projectCode?: string itemCode: string itemInfo: string + materialGroupCode?: string + materialGroupDescription?: string specification: string quantity: number quantityUnit: string @@ -49,6 +60,9 @@ interface ContractItemsTableProps { onTotalAmountChange: (total: number) => void availableBudget?: number readOnly?: boolean + contractScope?: string // 계약확정범위 (단가/금액/물량) + deliveryType?: string // 납기종류 (단일납기/분할납기) + contractDeliveryDate?: string // 기본정보의 계약납기일 } // 통화 목록 @@ -66,12 +80,28 @@ export function ContractItemsTable({ onItemsChange, onTotalAmountChange, availableBudget = 0, - readOnly = false + readOnly = false, + contractScope = '', + deliveryType = '', + contractDeliveryDate = '' }: ContractItemsTableProps) { + // 계약확정범위에 따른 필드 활성화/비활성화 + const isQuantityDisabled = contractScope === '단가' || contractScope === '물량' + const isTotalAmountDisabled = contractScope === '단가' || contractScope === '물량' + // 단일납기인 경우 납기일 필드 비활성화 및 기본값 설정 + const isDeliveryDateDisabled = deliveryType === '단일납기' const [localItems, setLocalItems] = React.useState(items) const [isSaving, setIsSaving] = React.useState(false) const [isLoading, setIsLoading] = React.useState(false) const [isEnabled, setIsEnabled] = React.useState(true) + const [showBatchInputDialog, setShowBatchInputDialog] = React.useState(false) + const [batchInputData, setBatchInputData] = React.useState({ + quantity: '', + quantityUnit: 'EA', + contractDeliveryDate: '', + contractCurrency: 'KRW', + contractUnitPrice: '' + }) // 초기 데이터 로드 React.useEffect(() => { @@ -79,21 +109,41 @@ export function ContractItemsTable({ try { setIsLoading(true) const fetchedItems = await getContractItems(contractId) - const formattedItems = fetchedItems.map(item => ({ - id: item.id, - itemCode: item.itemCode || '', - itemInfo: item.itemInfo || '', - specification: item.specification || '', - quantity: Number(item.quantity) || 0, - quantityUnit: item.quantityUnit || 'EA', - totalWeight: Number(item.totalWeight) || 0, - weightUnit: item.weightUnit || 'KG', - contractDeliveryDate: item.contractDeliveryDate || '', - contractUnitPrice: Number(item.contractUnitPrice) || 0, - contractAmount: Number(item.contractAmount) || 0, - contractCurrency: item.contractCurrency || 'KRW', - isSelected: false - })) as ContractItem[] + const formattedItems = fetchedItems.map(item => { + // itemInfo에서 자재그룹 정보 파싱 (형식: "자재그룹코드 / 자재그룹명") + let materialGroupCode = '' + let materialGroupDescription = '' + if (item.itemInfo) { + const parts = item.itemInfo.split(' / ') + if (parts.length >= 2) { + materialGroupCode = parts[0].trim() + materialGroupDescription = parts.slice(1).join(' / ').trim() + } else if (parts.length === 1) { + materialGroupCode = parts[0].trim() + } + } + + return { + id: item.id, + projectId: item.projectId || null, + projectName: item.projectName || undefined, + projectCode: item.projectCode || undefined, + itemCode: item.itemCode || '', + itemInfo: item.itemInfo || '', + materialGroupCode: materialGroupCode || undefined, + materialGroupDescription: materialGroupDescription || undefined, + specification: item.specification || '', + quantity: Number(item.quantity) || 0, + quantityUnit: item.quantityUnit || 'EA', + totalWeight: Number(item.totalWeight) || 0, + weightUnit: item.weightUnit || 'KG', + contractDeliveryDate: item.contractDeliveryDate || '', + contractUnitPrice: Number(item.contractUnitPrice) || 0, + contractAmount: Number(item.contractAmount) || 0, + contractCurrency: item.contractCurrency || 'KRW', + isSelected: false + } + }) as ContractItem[] setLocalItems(formattedItems as ContractItem[]) onItemsChange(formattedItems as ContractItem[]) } catch (error) { @@ -176,8 +226,11 @@ export function ContractItemsTable({ // 행 추가 const addRow = () => { const newItem: ContractItem = { + projectId: null, itemCode: '', itemInfo: '', + materialGroupCode: '', + materialGroupDescription: '', specification: '', quantity: 0, quantityUnit: 'EA', // 기본 수량 단위 @@ -218,6 +271,43 @@ export function ContractItemsTable({ onItemsChange(updatedItems) } + // 일괄입력 적용 + const applyBatchInput = () => { + if (localItems.length === 0) { + toast.error('품목이 없습니다. 먼저 품목을 추가해주세요.') + return + } + + const updatedItems = localItems.map(item => { + const updatedItem = { ...item } + + if (batchInputData.quantity) { + updatedItem.quantity = parseFloat(batchInputData.quantity) || 0 + } + if (batchInputData.quantityUnit) { + updatedItem.quantityUnit = batchInputData.quantityUnit + } + if (batchInputData.contractDeliveryDate) { + updatedItem.contractDeliveryDate = batchInputData.contractDeliveryDate + } + if (batchInputData.contractCurrency) { + updatedItem.contractCurrency = batchInputData.contractCurrency + } + if (batchInputData.contractUnitPrice) { + updatedItem.contractUnitPrice = parseFloat(batchInputData.contractUnitPrice) || 0 + // 단가가 변경되면 계약금액도 재계산 + updatedItem.contractAmount = updatedItem.contractUnitPrice * updatedItem.quantity + } + + return updatedItem + }) + + setLocalItems(updatedItems) + onItemsChange(updatedItems) + setShowBatchInputDialog(false) + toast.success('일괄입력이 적용되었습니다.') + } + // 통화 포맷팅 const formatCurrency = (amount: number, currency: string = 'KRW') => { @@ -292,6 +382,104 @@ export function ContractItemsTable({ 행 추가 + + + + + + + 품목 정보 일괄입력 + +
+
+ + setBatchInputData(prev => ({ ...prev, quantity: e.target.value }))} + placeholder="수량 입력 (선택사항)" + /> +
+
+ + +
+
+ + setBatchInputData(prev => ({ ...prev, contractDeliveryDate: e.target.value }))} + /> +
+
+ + +
+
+ + setBatchInputData(prev => ({ ...prev, contractUnitPrice: e.target.value }))} + placeholder="계약단가 입력 (선택사항)" + /> +
+
+ + +
+
+
+