diff --git a/apps/as/src/entities/elapr/index.ts b/apps/as/src/entities/elapr/index.ts index 777245649..2210dfc17 100644 --- a/apps/as/src/entities/elapr/index.ts +++ b/apps/as/src/entities/elapr/index.ts @@ -41,6 +41,7 @@ export { validateAllRowsForSave, validateAplnRow, validateAtrzOrder, + validateUniqueAtrzAprvr, validateUniqueSeqNo, } from "./lib/apln-validators"; export { diff --git a/apps/as/src/entities/elapr/lib/apln-validators.ts b/apps/as/src/entities/elapr/lib/apln-validators.ts index 8b75b4279..8ff1d997e 100644 --- a/apps/as/src/entities/elapr/lib/apln-validators.ts +++ b/apps/as/src/entities/elapr/lib/apln-validators.ts @@ -76,3 +76,16 @@ export function validateUniqueSeqNo(rows: ElaprAplnRow[]): string | null { } return null; } + +export function validateUniqueAtrzAprvr(rows: ElaprAplnRow[]): string | null { + const pairSet = new Set(); + for (const row of getVisibleRows(rows)) { + const atrz = row.atrzSeComCd?.trim(); + const aprvr = row.aprvrUuid?.trim(); + if (!atrz || !aprvr) continue; + const key = `${atrz}::${aprvr}`; + if (pairSet.has(key)) return "동일한 결재구분과 결재자가 중복됩니다."; + pairSet.add(key); + } + return null; +} diff --git a/apps/as/src/entities/elapr/ui/elapr-apln-picker-grid.tsx b/apps/as/src/entities/elapr/ui/elapr-apln-picker-grid.tsx index c8c84e1c6..f5a065ff6 100644 --- a/apps/as/src/entities/elapr/ui/elapr-apln-picker-grid.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-apln-picker-grid.tsx @@ -17,7 +17,7 @@ import { } from "@common/components"; import { useElaprAplnPage } from "../api/queries"; -import { validateAplnRow } from "../lib/apln-validators"; +import { validateAplnRow, validateUniqueAtrzAprvr } from "../lib/apln-validators"; import { useAplnRows } from "../lib/use-apln-rows"; import { useDndSensors } from "../lib/use-dnd-sensors"; import type { ElaprApln } from "../model/elapr-apln-interface"; @@ -110,6 +110,12 @@ function ElaprAplnPickerGrid({ return; } + const duplicateError = validateUniqueAtrzAprvr(rows); + if (duplicateError) { + alert(duplicateError); + return; + } + commitEdit(row.rowKey); } diff --git a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-picker-dialog.tsx b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-picker-dialog.tsx index 0d9b5161e..ec235ec5e 100644 --- a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-picker-dialog.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-picker-dialog.tsx @@ -17,6 +17,7 @@ import { getVisibleRows, validateAllRowsForSave, validateAtrzOrder, + validateUniqueAtrzAprvr, validateUniqueSeqNo, } from "../lib/apln-validators"; import { mapDocPrgrsToAplnRows } from "../lib/doc-prgrs-to-apln-rows"; @@ -90,6 +91,12 @@ function ElaprDocPrgrsPickerDialog({ return; } + const atrzAprvrError = validateUniqueAtrzAprvr(aplnRows); + if (atrzAprvrError) { + alert(atrzAprvrError); + return; + } + const visibleRows = getVisibleRows(aplnRows); onConfirm?.({ aplnRows: visibleRows }); diff --git a/apps/as/src/entities/elapr/ui/elapr-tmplt-picker-dialog.tsx b/apps/as/src/entities/elapr/ui/elapr-tmplt-picker-dialog.tsx index aa70932e9..b61672ef8 100644 --- a/apps/as/src/entities/elapr/ui/elapr-tmplt-picker-dialog.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-tmplt-picker-dialog.tsx @@ -17,6 +17,7 @@ import { getVisibleRows, validateAllRowsForSave, validateAtrzOrder, + validateUniqueAtrzAprvr, validateUniqueSeqNo, } from "../lib/apln-validators"; import type { ElaprAplnRow } from "../model/elapr-apln-row-types"; @@ -106,6 +107,12 @@ function ElaprTmpltPickerDialog({ return; } + const atrzAprvrError = validateUniqueAtrzAprvr(aplnRows); + if (atrzAprvrError) { + alert(atrzAprvrError); + return; + } + const visibleRows = getVisibleRows(aplnRows); const payload: ElaprTmpltPickerConfirmPayload = { diff --git a/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-apply-payload-schema.ts b/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-apply-payload-schema.ts index 709ae9012..75b2a1804 100644 --- a/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-apply-payload-schema.ts +++ b/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-apply-payload-schema.ts @@ -65,7 +65,7 @@ export const fnrsCnsltnSplmntDmndItemSchema = z.object({ splmntArtclComCd: z.string().nullish(), splmntDmndCn: z.string().optional(), splmntDmndDt: z.string().optional(), - splmntDmndAtchFileGroupId: z.string().optional(), + splmntDmndAtchFileGroupId: z.string().nullish(), splmntCn: z.string().optional(), splmntPicNm: z.string().optional(), splmntCmptnDt: z.string().optional(), diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx index 87a3dc4b0..cff36df8a 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx @@ -18,6 +18,7 @@ import { fnrsCnsltnMngService, } from "@/entities/fnrs-cnsltn"; import { useFnrsSharSeMngTree } from "@/entities/fnrs-shar-se"; +import { findCompletedPwrkDraft, usePwrkCnsltnMngByUpDocNo } from "@/entities/pwrk-cnsltn-mng"; import { AttachmentsFormSection, buildAcntgCostItems, @@ -35,7 +36,10 @@ import { } from "@/widgets/pbadms-sprt"; import { Button } from "@common/components"; -import { type FnrsCnsltnRegistFormData, fnrsCnsltnRegistSchema } from "../lib/fnrs-cnsltn-regist-schema"; +import { + type FnrsCnsltnRegistFormData, + fnrsCnsltnRegistSchema, +} from "../lib/fnrs-cnsltn-regist-schema"; import { buildFormPayload, getCreateDefaultValues, @@ -68,6 +72,12 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) const isSplmntPrgrsStts = initialData?.detDto?.prgrsSttsComCd === "SL"; const fnrsCnsltnDocNo = initialData?.detDto?.fnrsCnsltnDocNo; + const { data: pwrkCnsltnData } = usePwrkCnsltnMngByUpDocNo( + fnrsCnsltnDocNo, + isSplmntPrgrsStts && Boolean(fnrsCnsltnDocNo), + ); + const hasCompletedDraft = Boolean(findCompletedPwrkDraft(pwrkCnsltnData)); + const { data: masterSharTree = [] } = useFnrsSharSeMngTree(); const { data: acntgTree = [] } = useAcntgSeMngTree(); const { data: essentialFiles = [] } = useEsntlAtchFileMngTree({ docSeComCd: "FNRS" }); @@ -126,7 +136,7 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) (item) => !item.splmntCn || !item.splmntCn.trim(), ); if (hasEmptySplmntCn) { - alert("보완요청내용이 있습니다. 보완 완료내용을 입력해 주세요."); + alert("보완 요청 내용이 있습니다.\n보완 완료 내용을 입력해 주세요."); return; } @@ -305,7 +315,7 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) ) : ( <> - {fnrsCnsltnDocNo ? ( + {hasCompletedDraft ? (