fix: [material] 입고관리 코드 정리 + 단가목록 동기화버튼 주석처리

- 입고 상세/처리 다이얼로그 코드 간소화
- 단가 목록 품목마스터 동기화 버튼 주석처리 (미구현)
- 테넌트 모듈 분리 분석/계획 문서 추가
This commit is contained in:
유병철
2026-03-17 20:51:58 +09:00
parent 37f0e57b16
commit 1151fb0bf7
6 changed files with 2275 additions and 57 deletions

View File

@@ -91,16 +91,6 @@ const INITIAL_FORM_DATA: Partial<ReceivingDetailType> = {
inventoryAdjustments: [],
};
// 로트번호 생성 (YYMMDD-NN)
function generateLotNo(): string {
const now = new Date();
const yy = String(now.getFullYear()).slice(-2);
const mm = String(now.getMonth() + 1).padStart(2, '0');
const dd = String(now.getDate()).padStart(2, '0');
const seq = String(Math.floor(Math.random() * 100)).padStart(2, '0');
return `${yy}${mm}${dd}-${seq}`;
}
// localStorage에서 로그인 사용자 정보 가져오기
function getLoggedInUser(): { name: string; department: string } {
if (typeof window === 'undefined') return { name: '', department: '' };
@@ -169,7 +159,6 @@ export function ReceivingDetail({ id, mode = 'view' }: Props) {
const data = generateReceivingData();
setFormData((prev) => ({
...prev,
lotNo: generateLotNo(),
itemCode: data.itemCode,
itemName: data.itemName,
specification: data.specification,
@@ -574,15 +563,12 @@ export function ReceivingDetail({ id, mode = 'view' }: Props) {
/>
</div>
{/* 원자재로트 - 수정 가능 */}
{/* 원자재로트 - 자동채번 (readOnly) */}
<div>
<Label className="text-sm text-muted-foreground"></Label>
<Input
className="mt-1.5"
value={formData.lotNo || ''}
onChange={(e) => setFormData((prev) => ({ ...prev, lotNo: e.target.value }))}
placeholder="원자재로트를 입력하세요"
/>
<div className="mt-1.5 px-3 py-2 bg-gray-200 border border-gray-300 rounded-md text-sm text-gray-500 cursor-not-allowed select-none">
{formData.lotNo || (isNewMode ? '저장 시 자동 생성' : '-')}
</div>
</div>
{/* 품목코드 - 검색 모달 선택 */}

View File

@@ -23,16 +23,6 @@ import {
import { Alert, AlertDescription } from '@/components/ui/alert';
import type { ReceivingDetail, ReceivingProcessFormData } from './types';
// LOT 번호 생성 함수 (YYMMDD-NN 형식)
function generateLotNo(): string {
const now = new Date();
const yy = String(now.getFullYear()).slice(-2);
const mm = String(now.getMonth() + 1).padStart(2, '0');
const dd = String(now.getDate()).padStart(2, '0');
const random = String(Math.floor(Math.random() * 100)).padStart(2, '0');
return `${yy}${mm}${dd}-${random}`;
}
interface Props {
open: boolean;
onOpenChange: (open: boolean) => void;
@@ -42,7 +32,7 @@ interface Props {
export function ReceivingProcessDialog({ open, onOpenChange, detail, onComplete }: Props) {
// 폼 데이터
const [receivingLot, setReceivingLot] = useState(() => generateLotNo());
const [receivingLot, setReceivingLot] = useState('');
const [supplierLot, setSupplierLot] = useState('');
const [receivingQty, setReceivingQty] = useState<string>((detail.orderQty ?? 0).toString());
const [receivingLocation, setReceivingLocation] = useState('');
@@ -56,9 +46,7 @@ export function ReceivingProcessDialog({ open, onOpenChange, detail, onComplete
const validateForm = useCallback((): boolean => {
const errors: string[] = [];
if (!receivingLot.trim()) {
errors.push('입고LOT는 필수 입력 항목입니다.');
}
// 입고LOT는 API가 자동 채번하므로 필수 검증 제거
if (!receivingQty.trim() || isNaN(Number(receivingQty)) || Number(receivingQty) <= 0) {
errors.push('입고수량은 필수 입력 항목입니다. 유효한 숫자를 입력해주세요.');
@@ -68,7 +56,7 @@ export function ReceivingProcessDialog({ open, onOpenChange, detail, onComplete
setValidationErrors(errors);
return errors.length === 0;
}, [receivingLot, receivingQty]);
}, [receivingQty]);
// 입고 처리
const handleSubmit = useCallback(async () => {
@@ -161,16 +149,12 @@ export function ReceivingProcessDialog({ open, onOpenChange, detail, onComplete
{/* 입력 필드 */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label className="text-sm">
LOT <span className="text-red-500">*</span>
</Label>
<Label className="text-sm text-muted-foreground">LOT</Label>
<Input
value={receivingLot}
onChange={(e) => {
setReceivingLot(e.target.value);
setValidationErrors([]);
}}
placeholder="예: 251223-41"
readOnly
className="bg-gray-50"
placeholder="저장 시 자동 생성"
/>
</div>
<div className="space-y-2">

View File

@@ -497,7 +497,7 @@ function transformFrontendToApi(
if (data.remark !== undefined) result.remark = data.remark;
if (data.receivingQty !== undefined) result.receiving_qty = data.receivingQty;
if (data.receivingDate !== undefined) result.receiving_date = data.receivingDate;
if (data.lotNo !== undefined) result.lot_no = data.lotNo;
// lot_no는 API가 자동 채번하므로 프론트에서 전송하지 않음
if (data.supplierMaterialNo !== undefined) result.material_no = data.supplierMaterialNo;
if (data.manufacturer !== undefined) result.manufacturer = data.manufacturer;
if (data.certificateFileId !== undefined) result.certificate_file_id = data.certificateFileId;
@@ -511,7 +511,7 @@ function transformProcessDataToApi(
): Record<string, unknown> {
return {
receiving_qty: data.receivingQty,
lot_no: data.receivingLot,
// lot_no는 API가 자동 채번하므로 프론트에서 전송하지 않음
supplier_lot: data.supplierLot,
receiving_location: data.receivingLocation,
remark: data.remark,

View File

@@ -13,7 +13,6 @@ import {
Package,
AlertCircle,
CheckCircle2,
RefreshCw,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
@@ -326,18 +325,19 @@ export function PricingListClient({
};
// 헤더 액션 (함수로 정의)
const headerActions = () => (
<Button
variant="outline"
onClick={() => {
// TODO: API 연동 시 품목 마스터 동기화 로직 구현
}}
className="ml-auto gap-2"
>
<RefreshCw className="h-4 w-4" />
</Button>
);
// NOTE: 품목 마스터 동기화 버튼 - 실제 로직 미구현 상태로 주석처리
// const headerActions = () => (
// <Button
// variant="outline"
// onClick={() => {
// // TODO: API 연동 시 품목 마스터 동기화 로직 구현
// }}
// className="ml-auto gap-2"
// >
// <RefreshCw className="h-4 w-4" />
// 품목 마스터 동기화
// </Button>
// );
// UniversalListPage 설정
const pricingConfig: UniversalListConfig<PricingListItem> = {
@@ -356,7 +356,7 @@ export function PricingListClient({
},
columns: tableColumns,
headerActions,
// headerActions, // 품목 마스터 동기화 버튼 미구현으로 주석처리
stats,
tabs,