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 --- .../general-contract-subcontract-checklist.tsx | 610 +++++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 lib/general-contracts_old/detail/general-contract-subcontract-checklist.tsx (limited to 'lib/general-contracts_old/detail/general-contract-subcontract-checklist.tsx') diff --git a/lib/general-contracts_old/detail/general-contract-subcontract-checklist.tsx b/lib/general-contracts_old/detail/general-contract-subcontract-checklist.tsx new file mode 100644 index 00000000..ce7c8baf --- /dev/null +++ b/lib/general-contracts_old/detail/general-contract-subcontract-checklist.tsx @@ -0,0 +1,610 @@ +'use client' + +import React, { useState } from 'react' +import { Card, CardContent } from '@/components/ui/card' +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion' +import { Checkbox } from '@/components/ui/checkbox' +import { Label } from '@/components/ui/label' +import { Badge } from '@/components/ui/badge' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { Textarea } from '@/components/ui/textarea' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { Button } from '@/components/ui/button' +import { updateSubcontractChecklist } from '../service' +import { toast } from 'sonner' +import { AlertTriangle, CheckCircle, XCircle, HelpCircle, Save } from 'lucide-react' + +interface SubcontractChecklistData { + // 1. 계약서면발급 + contractDocumentIssuance: { + workOrderBeforeStart: boolean + entrustmentDetails: boolean + deliveryDetails: boolean + inspectionMethod: boolean + subcontractPayment: boolean + materialProvision: boolean + priceAdjustment: boolean + } + // 2. 부당하도급대금결정행위 + unfairSubcontractPricing: { + priceReductionWithBasis: boolean + noNegotiationAfterLowestBid: boolean + noDeceptionInPricing: boolean + noUniformPriceReduction: boolean + noDiscriminatoryTreatment: boolean + } + // 점검결과 + inspectionResult: 'compliant' | 'violation' | 'suspected_violation' + // 귀책부서 (위반/위반의심 시 필수) + responsibleDepartment?: string + // 원인 (위반/위반의심 시 필수) + cause?: string + causeOther?: string + // 대책 (위반/위반의심 시 필수) + countermeasure?: string + countermeasureOther?: string +} + +interface SubcontractChecklistProps { + contractId: number + onDataChange: (data: SubcontractChecklistData) => void + readOnly?: boolean + initialData?: SubcontractChecklistData +} + +export function SubcontractChecklist({ contractId, onDataChange, readOnly = false, initialData }: SubcontractChecklistProps) { + // 기본 데이터 구조 + const defaultData: SubcontractChecklistData = { + contractDocumentIssuance: { + workOrderBeforeStart: false, + entrustmentDetails: false, + deliveryDetails: false, + inspectionMethod: false, + subcontractPayment: false, + materialProvision: false, + priceAdjustment: false, + }, + unfairSubcontractPricing: { + priceReductionWithBasis: false, + noNegotiationAfterLowestBid: false, + noDeceptionInPricing: false, + noUniformPriceReduction: false, + noDiscriminatoryTreatment: false, + }, + inspectionResult: 'compliant', + } + + // initialData와 기본값을 깊이 병합 + const mergedInitialData = React.useMemo(() => { + if (!initialData) return defaultData + + return { + contractDocumentIssuance: { + ...defaultData.contractDocumentIssuance, + ...(initialData.contractDocumentIssuance || {}), + }, + unfairSubcontractPricing: { + ...defaultData.unfairSubcontractPricing, + ...(initialData.unfairSubcontractPricing || {}), + }, + inspectionResult: initialData.inspectionResult || defaultData.inspectionResult, + responsibleDepartment: initialData.responsibleDepartment, + cause: initialData.cause, + causeOther: initialData.causeOther, + countermeasure: initialData.countermeasure, + countermeasureOther: initialData.countermeasureOther, + } + }, [initialData]) + + const [isEnabled, setIsEnabled] = useState(true) + const [data, setData] = useState(mergedInitialData) + + // 점검결과 자동 계산 함수 + const calculateInspectionResult = ( + contractDocumentIssuance: SubcontractChecklistData['contractDocumentIssuance'], + unfairSubcontractPricing: SubcontractChecklistData['unfairSubcontractPricing'] + ): 'compliant' | 'violation' | 'suspected_violation' => { + // 1. 계약서면발급의 모든 항목이 체크되어야 함 + const allContractItemsChecked = Object.values(contractDocumentIssuance).every(checked => checked) + + // 2. 부당하도급대금결정행위에서 'X' 항목 체크 확인 + const hasUnfairPricingViolation = Object.values(unfairSubcontractPricing).some(checked => !checked) + + if (!allContractItemsChecked) { + return 'violation' + } else if (hasUnfairPricingViolation) { + return 'suspected_violation' + } + + return 'compliant' + } + + const handleContractDocumentChange = (field: keyof SubcontractChecklistData['contractDocumentIssuance'], checked: boolean) => { + setData(prev => { + const newContractDocumentIssuance = { + ...prev.contractDocumentIssuance, + [field]: checked + } + const newInspectionResult = calculateInspectionResult(newContractDocumentIssuance, prev.unfairSubcontractPricing) + + return { + ...prev, + contractDocumentIssuance: newContractDocumentIssuance, + inspectionResult: newInspectionResult + } + }) + } + + const handleUnfairPricingChange = (field: keyof SubcontractChecklistData['unfairSubcontractPricing'], checked: boolean) => { + setData(prev => { + const newUnfairSubcontractPricing = { + ...prev.unfairSubcontractPricing, + [field]: checked + } + const newInspectionResult = calculateInspectionResult(prev.contractDocumentIssuance, newUnfairSubcontractPricing) + + return { + ...prev, + unfairSubcontractPricing: newUnfairSubcontractPricing, + inspectionResult: newInspectionResult + } + }) + } + + const handleFieldChange = (field: keyof SubcontractChecklistData, value: string) => { + setData(prev => ({ ...prev, [field]: value })) + } + + // 데이터 변경 시 부모 컴포넌트에 전달 (저장 시에만) + const handleSave = async () => { + try { + // validation 체크 + const errors = [] + + // 위반 또는 위반의심인 경우 필수 필드 체크 + if (data.inspectionResult === 'violation' || data.inspectionResult === 'suspected_violation') { + if (!data.responsibleDepartment) errors.push('귀책부서') + if (!data.cause) errors.push('원인') + if (!data.countermeasure) errors.push('대책') + + // 기타 선택 시 추가 입력 필드 체크 + if (data.cause === '기타' && !data.causeOther) errors.push('원인 기타 입력') + if (data.countermeasure === '기타' && !data.countermeasureOther) errors.push('대책 기타 입력') + } + + if (errors.length > 0) { + toast.error(`다음 항목을 입력해주세요: ${errors.join(', ')}`) + return + } + + await updateSubcontractChecklist(contractId, data) + onDataChange(data) + toast.success('하도급법 체크리스트가 저장되었습니다.') + } catch (error) { + console.error('Error saving subcontract checklist:', error) + toast.error('하도급법 체크리스트 저장 중 오류가 발생했습니다.') + } + } + + const getInspectionResultInfo = () => { + switch (data.inspectionResult) { + case 'compliant': + return { + icon: , + label: '준수', + color: 'bg-green-100 text-green-800', + description: '1. 계약서면발급의 모든 항목에 체크, 2. 부당하도급에서 X항목에 체크한 상태' + } + case 'violation': + return { + icon: , + label: '위반', + color: 'bg-red-100 text-red-800', + description: '1. 계약서면발급의 모든 항목 중 1개 이상 미체크 한 경우' + } + case 'suspected_violation': + return { + icon: , + label: '위반의심', + color: 'bg-yellow-100 text-yellow-800', + description: '2. 부당하도급에서 O항목에 체크한 경우' + } + default: + // 기본값으로 준수 상태 반환 + return { + icon: , + label: '준수', + color: 'bg-green-100 text-green-800', + description: '점검 결과가 유효하지 않습니다.' + } + } + } + + const resultInfo = getInspectionResultInfo() + const isViolationOrSuspected = data.inspectionResult === 'violation' || data.inspectionResult === 'suspected_violation' + + const causeOptions = [ + { value: '서면미교부_현업부서 하도급법 이해 부족', label: '서면미교부_현업부서 하도급법 이해 부족' }, + { value: '서면미교부_기존계약 만료前 계약연장에 대한 사전조치 소홀', label: '서면미교부_기존계약 만료前 계약연장에 대한 사전조치 소홀' }, + { value: '서면미교부_긴급작업時 先작업합의서 체결 절차 未인지', label: '서면미교부_긴급작업時 先작업합의서 체결 절차 未인지' }, + { value: '부당가격인하_예산부족 等 원가절감 필요성 대두', label: '부당가격인하_예산부족 等 원가절감 필요성 대두' }, + { value: '부당가격인하_하도급법 이해부족 및 금액 협의과정에 대한 근거 미흡', label: '부당가격인하_하도급법 이해부족 및 금액 협의과정에 대한 근거 미흡' }, + { value: '기타', label: '기타' } + ] + + const countermeasureOptions = [ + { value: '서면미교부_준법지원을 통한 현업부서 계몽활동 실시', label: '서면미교부_준법지원을 통한 현업부서 계몽활동 실시' }, + { value: '서면미교부_계약만료일정 별도 관리 및 사전점검', label: '서면미교부_계약만료일정 별도 관리 및 사전점검' }, + { value: '서면미교부_작업착수前 先작업합의서 체결토록 현업에 가이드', label: '서면미교부_작업착수前 先작업합의서 체결토록 현업에 가이드' }, + { value: '부당가격인하_최종 협의된 견적서 접수/보관 必', label: '부당가격인하_최종 협의된 견적서 접수/보관 必' }, + { value: '부당가격인하_합의서 체결시 \'자율적 의사결정\' 等 문구 삽입', label: '부당가격인하_합의서 체결시 \'자율적 의사결정\' 等 문구 삽입' }, + { value: '부당가격인하_수의계약時 금액 협의과정에 대한 근거 확보 (회의록, 메일, 당초/변경 견적서 等)', label: '부당가격인하_수의계약時 금액 협의과정에 대한 근거 확보 (회의록, 메일, 당초/변경 견적서 等)' }, + { value: '기타', label: '기타' } + ] + + return ( + + + +
+ + 하도급법 자율점검 체크리스트 + + {resultInfo.label} + +
+
+ + + + {/* 체크박스 */} +
+ setIsEnabled(checked as boolean)} + disabled={readOnly} + /> + 하도급법 자율점검 체크리스트 활성화 +
+ + {/* 점검결과 표시 */} +
+
+ {resultInfo.icon} + + {resultInfo.label} + +
+
+ {resultInfo.description} +
+
+ + + {/* 1. 계약서면발급 */} + + + 1. 계약서면발급 + + +
+ + + 본 계약에 해당하는 항목을 아래 안내사항에 따라 'O'인 경우 체크하세요. + + + +
+
+ handleContractDocumentChange('workOrderBeforeStart', checked as boolean)} + disabled={!isEnabled || readOnly} + /> +
+ +

+ ※ 단가, 물량 등을 정하지 못하는 경우 정하는 기일을 기재 +

+
+
+ +
+ handleContractDocumentChange('entrustmentDetails', checked as boolean)} + disabled={!isEnabled || readOnly} + /> +
+ +
+
+ +
+ handleContractDocumentChange('deliveryDetails', checked as boolean)} + disabled={!isEnabled || readOnly} + /> +
+ +

+ 예: 삼성의 검사완료(승인) 후 목적물 인도 등 +

+
+
+ +
+ handleContractDocumentChange('inspectionMethod', checked as boolean)} + disabled={!isEnabled || readOnly} + /> +
+ +

+ 예: 작업완료 후 삼성담당자 입회하에 검사를 실시하고 10일 이내 검사결과 통보 +

+
+
+ +
+ handleContractDocumentChange('subcontractPayment', checked as boolean)} + disabled={!isEnabled || readOnly} + /> +
+ +
+
+ +
+ handleContractDocumentChange('materialProvision', checked as boolean)} + disabled={!isEnabled || readOnly} + /> +
+ +

+ 해당사항 없을 시에도 기재로 간주 +

+
+
+ +
+ handleContractDocumentChange('priceAdjustment', checked as boolean)} + disabled={!isEnabled || readOnly} + /> +
+ +
+
+
+
+
+
+ + {/* 2. 부당하도급대금결정행위 */} + + + 2. 부당하도급대금결정행위 + + +
+ + + 본 계약에 해당하는 항목을 아래 안내사항에 따라 'O'인 경우 체크하세요. +
+ ※ 'X' 항목에 다음 안내사항이 자동 표기됩니다: +
+
+ +
+

안내사항:

+
    +
  • • 단가 인하時 객관/타당한 근거에 의해 산출하고 협력사와 합의
  • +
  • • 최저가 경쟁입찰 후 입찰자와 대금인하 협상 불가
  • +
  • • 협력사에 발주량 등 거래조건에 착오를 일으키게 하거나 타 사업자 견적 또는 거짓 견적을 보여주는 등 기만하여 대금을 결정할 수 없음
  • +
  • • 정당한 이유 없이 일률적 비율로 단가 인하 불가
  • +
  • • 정당한 이유 없이 특정 사업자를 차별 취급 하도록 대금 결정 불가
  • +
+
+ +
+
+ handleUnfairPricingChange('priceReductionWithBasis', checked as boolean)} + disabled={!isEnabled || readOnly} + /> + +
+ +
+ handleUnfairPricingChange('noNegotiationAfterLowestBid', checked as boolean)} + disabled={!isEnabled || readOnly} + /> + +
+ +
+ handleUnfairPricingChange('noDeceptionInPricing', checked as boolean)} + disabled={!isEnabled || readOnly} + /> + +
+ +
+ handleUnfairPricingChange('noUniformPriceReduction', checked as boolean)} + disabled={!isEnabled || readOnly} + /> + +
+ +
+ handleUnfairPricingChange('noDiscriminatoryTreatment', checked as boolean)} + disabled={!isEnabled || readOnly} + /> + +
+
+
+
+
+ + {/* 위반/위반의심 시 추가 정보 */} + {isViolationOrSuspected && ( + + + 위반/위반의심 상세 정보 + + +
+ + + + 점검결과가 위반 또는 위반의심인 경우 아래 정보를 필수로 입력해주세요. + + + +
+
+ +